2 * Copyright (C) 2013-2016 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
32import SessionBroadcast 0.1
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
44import Unity.Indicators 0.1 as Indicators
46import WindowManager 1.0
52 theme.name: "Ubuntu.Components.Themes.SuruDark"
54 // to be set from outside
55 property int orientationAngle: 0
56 property int orientation
57 property Orientations orientations
58 property real nativeWidth
59 property real nativeHeight
60 property alias panelAreaShowProgress: panel.panelAreaShowProgress
61 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
62 property string mode: "full-greeter"
63 property alias oskEnabled: inputMethod.enabled
64 function updateFocusedAppOrientation() {
65 stage.updateFocusedAppOrientation();
67 function updateFocusedAppOrientationAnimated() {
68 stage.updateFocusedAppOrientationAnimated();
70 property bool hasMouse: false
71 property bool hasKeyboard: false
73 // to be read from outside
74 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
76 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
77 && stage.orientationChangesEnabled
78 && (!greeter || !greeter.animating)
80 readonly property bool showingGreeter: greeter && greeter.shown
82 property bool startingUp: true
83 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
85 property int supportedOrientations: {
87 // Ensure we don't rotate during start up
88 return Qt.PrimaryOrientation;
89 } else if (showingGreeter || notifications.topmostIsFullscreen) {
90 return Qt.PrimaryOrientation;
92 return shell.orientations.map(stage.supportedOrientations);
96 readonly property var mainApp: stage.mainApp
100 _onMainAppChanged(mainApp.appId);
104 target: ApplicationManager
106 if (shell.mainApp && shell.mainApp.appId === appId) {
107 _onMainAppChanged(appId);
111 function _onMainAppChanged(appId) {
112 if (wizard.active && appId != "" && appId != "unity8-dash") {
113 // If this happens on first boot, we may be in the
114 // wizard while receiving a call. But a call is more
115 // important than the wizard so just bail out of it.
119 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
120 // If we are in the middle of a call, make dialer lockedApp and show it.
121 // This can happen if user backs out of dialer back to greeter, then
122 // launches dialer again.
123 greeter.lockedApp = appId;
125 greeter.notifyAppFocusRequested(appId);
127 panel.indicators.hide();
128 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
131 // For autopilot consumption
132 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
134 // Note when greeter is waiting on PAM, so that we can disable edges until
135 // we know which user data to show and whether the session is locked.
136 readonly property bool waitingOnGreeter: greeter && greeter.waiting
138 property real edgeSize: units.gu(settings.edgeDragWidth)
141 id: wallpaperResolver
142 objectName: "wallpaperResolver"
144 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
145 readonly property bool hasCustomBackground: background != defaultBackground
147 // Use a cached version of the scaled-down wallpaper (as sometimes the
148 // image can be quite big compared to the device size, including for
149 // our default wallpaper). We use a name=wallpaper argument here to
150 // make sure we don't litter our cache with lots of scaled images. We
151 // only need to bother caching one at a time.
152 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
155 id: backgroundSettings
156 schema.id: "org.gnome.desktop.background"
160 AccountsService.backgroundFile,
161 backgroundSettings.pictureUri,
166 readonly property alias greeter: greeterLoader.item
168 function activateApplication(appId) {
169 // Either open the app in our own session, or -- if we're acting as a
170 // greeter -- ask the user's session to open it for us.
171 if (shell.mode === "greeter") {
172 activateURL("application:///" + appId + ".desktop");
178 function activateURL(url) {
179 SessionBroadcast.requestUrlStart(AccountsService.user, url);
180 greeter.notifyUserRequestedApp();
181 panel.indicators.hide();
184 function startApp(appId) {
185 if (ApplicationManager.findApplication(appId)) {
186 ApplicationManager.requestFocusApplication(appId);
188 ApplicationManager.startApplication(appId);
192 function startLockedApp(app) {
193 if (greeter.locked) {
194 greeter.lockedApp = app;
196 startApp(app); // locked apps are always in our same session
200 target: LauncherModel
201 property: "applicationManager"
202 value: ApplicationManager
205 Component.onCompleted: {
206 finishStartUpTimer.start();
215 objectName: "dashCommunicator"
219 id: physicalKeysMapper
220 objectName: "physicalKeysMapper"
222 onPowerKeyLongPressed: dialogs.showPowerDialog();
223 onVolumeDownTriggered: volumeControl.volumeDown();
224 onVolumeUpTriggered: volumeControl.volumeUp();
225 onScreenshotTriggered: itemGrabber.capture(shell);
229 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
234 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
235 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
239 objectName: "windowInputMonitor"
240 onHomeKeyActivated: {
241 // Ignore when greeter is active, to avoid pocket presses
242 if (!greeter.active) {
244 ApplicationManager.requestFocusApplication("unity8-dash");
247 onTouchBegun: { cursor.opacity = 0; }
249 // move the (hidden) cursor to the last known touch position
250 var mappedCoords = mapFromItem(null, pos.x, pos.y);
251 cursor.x = mappedCoords.x;
252 cursor.y = mappedCoords.y;
253 cursor.mouseNeverMoved = false;
259 schema.id: "com.canonical.Unity8"
266 height: parent.height
270 objectName: "surfaceManager"
272 TopLevelWindowModel {
273 id: topLevelSurfaceList
274 objectName: "topLevelSurfaceList"
275 applicationManager: ApplicationManager // it's a singleton
276 surfaceManager: surfaceMan
285 dragAreaWidth: shell.edgeSize
286 background: wallpaperResolver.background
288 applicationManager: ApplicationManager
289 topLevelSurfaceList: topLevelSurfaceList
290 inputMethodRect: inputMethod.visibleRect
292 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
294 : shell.usageScenario
296 mode: usageScenario == "phone" ? "staged"
297 : usageScenario == "tablet" ? "stagedWithSideStage"
300 shellOrientation: shell.orientation
301 shellOrientationAngle: shell.orientationAngle
302 orientations: shell.orientations
303 nativeWidth: shell.nativeWidth
304 nativeHeight: shell.nativeHeight
306 interactive: (!greeter || !greeter.shown)
307 && panel.indicators.fullyClosed
308 && !notifications.useModal
310 onInteractiveChanged: { if (interactive) { focus = true; } }
312 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
313 suspended: greeter.shown
314 altTabPressed: physicalKeysMapper.altTabPressed
315 oskEnabled: shell.oskEnabled
316 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
318 onSpreadShownChanged: {
319 panel.indicators.hide();
320 panel.applicationMenus.hide();
327 minimumTouchPoints: 4
328 maximumTouchPoints: minimumTouchPoints
330 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
331 touchPoints.length >= minimumTouchPoints &&
332 touchPoints.length <= maximumTouchPoints
333 property bool wasPressed: false
335 onRecognisedPressChanged: {
336 if (recognisedPress) {
342 if (status !== TouchGestureArea.Recognized) {
343 if (status === TouchGestureArea.WaitingForTouch) {
344 if (wasPressed && !dragging) {
345 launcher.openDrawer(true);
356 objectName: "inputMethod"
357 surface: topLevelSurfaceList.inputMethodSurface
360 topMargin: panel.panelHeight
361 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
363 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
368 objectName: "greeterLoader"
370 anchors.topMargin: panel.panelHeight
371 sourceComponent: shell.mode != "shell" ? integratedGreeter :
372 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
374 item.objectName = "greeter"
376 property bool openDrawerAfterUnlock: false
380 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
381 launcher.openDrawer(false);
382 greeterLoader.openDrawerAfterUnlock = false;
389 id: integratedGreeter
392 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
393 hides: [launcher, panel.indicators, panel.applicationMenus]
394 tabletMode: shell.usageScenario != "phone"
395 forcedUnlock: wizard.active || shell.mode === "full-shell"
396 background: wallpaperResolver.cachedBackground
397 hasCustomBackground: wallpaperResolver.hasCustomBackground
398 allowFingerprint: !dialogs.hasActiveDialog &&
399 !notifications.topmostIsFullscreen &&
400 !panel.indicators.shown
402 // avoid overlapping with Launcher's edge drag area
403 // FIXME: Fix TouchRegistry & friends and remove this workaround
404 // Issue involves launcher's DDA getting disabled on a long
406 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
413 if (!tutorial.running) {
418 onEmergencyCall: startLockedApp("dialer-app")
423 // See powerConnection for why this is useful
424 id: showGreeterDelayed
436 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
437 // We just received an incoming call while locked. The
438 // indicator will have already launched dialer-app for us, but
439 // there is a race between "hasCalls" changing and the dialer
440 // starting up. So in case we lose that race, we'll start/
441 // focus the dialer ourselves here too. Even if the indicator
442 // didn't launch the dialer for some reason (or maybe a call
443 // started via some other means), if an active call is
444 // happening, we want to be in the dialer.
445 startLockedApp("dialer-app")
455 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
456 !callManager.hasCalls && !wizard.active) {
457 // We don't want to simply call greeter.showNow() here, because
458 // that will take too long. Qt will delay button event
459 // handling until the greeter is done loading and may think the
460 // user held down the power button the whole time, leading to a
461 // power dialog being shown. Instead, delay showing the
462 // greeter until we've finished handling the event. We could
463 // make the greeter load asynchronously instead, but that
464 // introduces a whole host of timing issues, especially with
465 // its animations. So this is simpler.
466 showGreeterDelayed.start();
471 function showHome() {
472 greeter.notifyUserRequestedApp();
474 if (shell.mode === "greeter") {
475 SessionBroadcast.requestHomeShown(AccountsService.user);
477 if (!greeter.active) {
478 launcher.openDrawer(false);
480 greeterLoader.openDrawerAfterUnlock = true;
494 anchors.fill: parent //because this draws indicator menus
496 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
497 minimizedPanelHeight: units.gu(3)
498 expandedPanelHeight: units.gu(7)
499 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
500 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
504 available: tutorial.panelEnabled
505 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
506 && (!greeter || !greeter.hasLockedApp)
507 && !shell.waitingOnGreeter
508 && settings.enableIndicatorMenu
510 model: Indicators.IndicatorsModel {
511 // tablet and phone both use the same profile
512 // FIXME: use just "phone" for greeter too, but first fix
513 // greeter app launching to either load the app inside the
514 // greeter or tell the session to load the app. This will
515 // involve taking the url-dispatcher dbus name and using
516 // SessionBroadcast to tell the session.
517 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
518 Component.onCompleted: load();
524 available: (!greeter || !greeter.shown)
525 && !shell.waitingOnGreeter
528 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
529 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
531 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
532 || greeter.hasLockedApp
533 greeterShown: greeter && greeter.shown
538 objectName: "launcher"
540 anchors.top: parent.top
541 anchors.topMargin: inverted ? 0 : panel.panelHeight
542 anchors.bottom: parent.bottom
544 dragAreaWidth: shell.edgeSize
545 available: tutorial.launcherEnabled
546 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
547 && !greeter.hasLockedApp
548 && !shell.waitingOnGreeter
549 && settings.enableLauncher
550 inverted: shell.usageScenario !== "desktop"
551 superPressed: physicalKeysMapper.superPressed
552 superTabPressed: physicalKeysMapper.superTabPressed
553 panelWidth: units.gu(settings.launcherWidth)
554 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
555 blurSource: greeter.shown ? greeter : stages
556 topPanelHeight: panel.panelHeight
557 drawerEnabled: !greeter.active
559 onShowDashHome: showHome()
560 onLauncherApplicationSelected: {
561 greeter.notifyUserRequestedApp();
562 shell.activateApplication(appId);
566 panel.indicators.hide()
576 shortcut: Qt.MetaModifier | Qt.Key_A
578 launcher.openDrawer(true);
582 shortcut: Qt.AltModifier | Qt.Key_F1
584 launcher.openForKeyboardNavigation();
588 shortcut: Qt.MetaModifier | Qt.Key_0
590 if (LauncherModel.get(9)) {
591 activateApplication(LauncherModel.get(9).appId);
598 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
600 if (LauncherModel.get(index)) {
601 activateApplication(LauncherModel.get(index).appId);
608 KeyboardShortcutsOverlay {
609 objectName: "shortcutsOverlay"
610 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
611 && height < parent.height - padding - panel.panelHeight
612 anchors.centerIn: parent
613 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
614 anchors.verticalCenterOffset: panel.panelHeight/2
616 opacity: enabled ? 0.95 : 0
618 Behavior on opacity {
619 UbuntuNumberAnimation {}
625 objectName: "tutorial"
628 paused: callManager.hasCalls || !greeter || greeter.active ||
630 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
631 inputMethod.visible ||
632 (launcher.shown && !launcher.lockedVisible) ||
633 panel.indicators.shown || stage.rightEdgeDragProgress > 0
634 usageScenario: shell.usageScenario
635 lastInputTimestamp: inputFilter.lastInputTimestamp
645 deferred: shell.mode === "greeter"
647 function unlockWhenDoneWithWizard() {
649 Connectivity.unlockAllModems();
653 Component.onCompleted: unlockWhenDoneWithWizard()
654 onActiveChanged: unlockWhenDoneWithWizard()
657 MouseArea { // modal notifications prevent interacting with other contents
659 visible: notifications.useModal
666 model: NotificationBackend.Model
668 hasMouse: shell.hasMouse
669 background: wallpaperResolver.cachedBackground
671 y: topmostIsFullscreen ? 0 : panel.panelHeight
672 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
677 when: overlay.width <= units.gu(60)
679 target: notifications
680 anchors.left: parent.left
681 anchors.right: parent.right
686 when: overlay.width > units.gu(60)
688 target: notifications
689 anchors.left: undefined
690 anchors.right: parent.right
692 PropertyChanges { target: notifications; width: units.gu(38) }
700 objectName: "dialogs"
702 visible: hasActiveDialog
704 usageScenario: shell.usageScenario
705 hasKeyboard: shell.hasKeyboard
707 shutdownFadeOutRectangle.enabled = true;
708 shutdownFadeOutRectangle.visible = true;
709 shutdownFadeOut.start();
714 target: SessionBroadcast
715 onShowHome: if (shell.mode !== "greeter") showHome()
720 objectName: "urlDispatcher"
721 active: shell.mode === "greeter"
722 onUrlRequested: shell.activateURL(url)
729 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
732 ignoreUnknownSignals: true
733 onItemSnapshotRequested: itemGrabber.capture(item)
738 id: cursorHidingTimer
740 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
741 onTriggered: cursor.opacity = 0;
747 visible: shell.hasMouse
749 topBoundaryOffset: panel.panelHeight
751 confiningItem: stage.itemConfiningMouseCursor
753 property bool mouseNeverMoved: true
755 target: cursor; property: "x"; value: shell.width / 2
756 when: cursor.mouseNeverMoved && cursor.visible
759 target: cursor; property: "y"; value: shell.height / 2
760 when: cursor.mouseNeverMoved && cursor.visible
765 readonly property var previewRectangle: stage.previewRectangle.target &&
766 stage.previewRectangle.target.dragging ?
767 stage.previewRectangle : null
769 onPushedLeftBoundary: {
770 if (buttons === Qt.NoButton) {
771 launcher.pushEdge(amount);
772 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
773 previewRectangle.maximizeLeft(amount);
777 onPushedRightBoundary: {
778 if (buttons === Qt.NoButton) {
779 stage.pushRightEdge(amount);
780 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
781 previewRectangle.maximizeRight(amount);
785 onPushedTopBoundary: {
786 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
787 previewRectangle.maximize(amount);
790 onPushedTopLeftCorner: {
791 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
792 previewRectangle.maximizeTopLeft(amount);
795 onPushedTopRightCorner: {
796 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
797 previewRectangle.maximizeTopRight(amount);
800 onPushedBottomLeftCorner: {
801 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
802 previewRectangle.maximizeBottomLeft(amount);
805 onPushedBottomRightCorner: {
806 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
807 previewRectangle.maximizeBottomRight(amount);
811 if (previewRectangle) {
812 previewRectangle.stop();
817 mouseNeverMoved = false;
821 Behavior on opacity { UbuntuNumberAnimation {} }
824 // non-visual objects
826 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
831 id: shutdownFadeOutRectangle
838 NumberAnimation on opacity {
843 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
844 DBusUnitySessionService.shutdown();
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list