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 "Components/PanelState"
42import Unity.Notifications 1.0 as NotificationBackend
43import Unity.Session 0.1
44import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
47import WindowManager 1.0
48import Unity.Debug 0.1 as Debug
54 theme.name: "Ubuntu.Components.Themes.SuruDark"
56 // to be set from outside
57 property int orientationAngle: 0
58 property int orientation
59 property Orientations orientations
60 property real nativeWidth
61 property real nativeHeight
62 property alias panelAreaShowProgress: panel.panelAreaShowProgress
63 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
64 property string mode: "full-greeter"
65 property alias oskEnabled: inputMethod.enabled
66 function updateFocusedAppOrientation() {
67 stage.updateFocusedAppOrientation();
69 function updateFocusedAppOrientationAnimated() {
70 stage.updateFocusedAppOrientationAnimated();
72 property bool hasMouse: false
73 property bool hasKeyboard: false
74 property bool hasTouchscreen: false
76 // to be read from outside
77 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
79 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
80 && stage.orientationChangesEnabled
81 && (!greeter || !greeter.animating)
83 readonly property bool showingGreeter: greeter && greeter.shown
85 property bool startingUp: true
86 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
88 property int supportedOrientations: {
90 // Ensure we don't rotate during start up
91 return Qt.PrimaryOrientation;
92 } else if (showingGreeter || notifications.topmostIsFullscreen) {
93 return Qt.PrimaryOrientation;
95 return shell.orientations.map(stage.supportedOrientations);
99 readonly property var mainApp: stage.mainApp
101 readonly property var topLevelSurfaceList: {
102 if (!WMScreen.currentWorkspace) return null;
103 return WMScreen.currentWorkspace.windowModel
108 _onMainAppChanged(mainApp.appId);
112 target: ApplicationManager
114 if (shell.mainApp && shell.mainApp.appId === appId) {
115 _onMainAppChanged(appId);
119 function _onMainAppChanged(appId) {
120 if (wizard.active && appId != "" && appId != "unity8-dash") {
121 // If this happens on first boot, we may be in the
122 // wizard while receiving a call. But a call is more
123 // important than the wizard so just bail out of it.
127 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
128 // If we are in the middle of a call, make dialer lockedApp and show it.
129 // This can happen if user backs out of dialer back to greeter, then
130 // launches dialer again.
131 greeter.lockedApp = appId;
133 greeter.notifyAppFocusRequested(appId);
135 panel.indicators.hide();
136 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
139 // For autopilot consumption
140 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
142 // Note when greeter is waiting on PAM, so that we can disable edges until
143 // we know which user data to show and whether the session is locked.
144 readonly property bool waitingOnGreeter: greeter && greeter.waiting
146 property real edgeSize: units.gu(settings.edgeDragWidth)
149 id: wallpaperResolver
150 objectName: "wallpaperResolver"
152 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
153 readonly property bool hasCustomBackground: background != defaultBackground
155 // Use a cached version of the scaled-down wallpaper (as sometimes the
156 // image can be quite big compared to the device size, including for
157 // our default wallpaper). We use a name=wallpaper argument here to
158 // make sure we don't litter our cache with lots of scaled images. We
159 // only need to bother caching one at a time.
160 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
163 id: backgroundSettings
164 schema.id: "org.gnome.desktop.background"
168 AccountsService.backgroundFile,
169 backgroundSettings.pictureUri,
174 readonly property alias greeter: greeterLoader.item
176 function activateApplication(appId) {
177 // Either open the app in our own session, or -- if we're acting as a
178 // greeter -- ask the user's session to open it for us.
179 if (shell.mode === "greeter") {
180 activateURL("application:///" + appId + ".desktop");
186 function activateURL(url) {
187 SessionBroadcast.requestUrlStart(AccountsService.user, url);
188 greeter.notifyUserRequestedApp();
189 panel.indicators.hide();
192 function startApp(appId) {
193 if (ApplicationManager.findApplication(appId)) {
194 ApplicationManager.requestFocusApplication(appId);
196 print("calling startApplication. active workspace is", WorkspaceManager.activeWorkspace)
197 ApplicationManager.startApplication(appId);
201 function startLockedApp(app) {
202 if (greeter.locked) {
203 greeter.lockedApp = app;
205 startApp(app); // locked apps are always in our same session
209 target: LauncherModel
210 property: "applicationManager"
211 value: ApplicationManager
214 Component.onCompleted: {
215 finishStartUpTimer.start();
224 objectName: "dashCommunicator"
228 id: physicalKeysMapper
229 objectName: "physicalKeysMapper"
231 onPowerKeyLongPressed: dialogs.showPowerDialog();
232 onVolumeDownTriggered: volumeControl.volumeDown();
233 onVolumeUpTriggered: volumeControl.volumeUp();
234 onScreenshotTriggered: itemGrabber.capture(shell);
238 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
243 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
244 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
248 objectName: "windowInputMonitor"
249 onHomeKeyActivated: {
250 // Ignore when greeter is active, to avoid pocket presses
251 if (!greeter.active) {
253 ApplicationManager.requestFocusApplication("unity8-dash");
256 onTouchBegun: { cursor.opacity = 0; }
258 // move the (hidden) cursor to the last known touch position
259 var mappedCoords = mapFromItem(null, pos.x, pos.y);
260 cursor.x = mappedCoords.x;
261 cursor.y = mappedCoords.y;
262 cursor.mouseNeverMoved = false;
268 schema.id: "com.canonical.Unity8"
273 objectName: "panelState"
280 height: parent.height
288 dragAreaWidth: shell.edgeSize
289 background: wallpaperResolver.background
291 applicationManager: ApplicationManager
292 topLevelSurfaceList: shell.topLevelSurfaceList
293 inputMethodRect: inputMethod.visibleRect
294 rightEdgePushProgress: rightEdgeBarrier.progress
296 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
298 : shell.usageScenario
300 mode: usageScenario == "phone" ? "staged"
301 : usageScenario == "tablet" ? "stagedWithSideStage"
304 shellOrientation: shell.orientation
305 shellOrientationAngle: shell.orientationAngle
306 orientations: shell.orientations
307 nativeWidth: shell.nativeWidth
308 nativeHeight: shell.nativeHeight
310 interactive: (!greeter || !greeter.shown)
311 && panel.indicators.fullyClosed
312 && !notifications.useModal
314 onInteractiveChanged: { if (interactive) { focus = true; } }
316 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
317 suspended: greeter.shown
318 altTabPressed: physicalKeysMapper.altTabPressed
319 oskEnabled: shell.oskEnabled
320 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
321 panelState: panelState
323 onSpreadShownChanged: {
324 panel.indicators.hide();
325 panel.applicationMenus.hide();
332 minimumTouchPoints: 4
333 maximumTouchPoints: minimumTouchPoints
335 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
336 touchPoints.length >= minimumTouchPoints &&
337 touchPoints.length <= maximumTouchPoints
338 property bool wasPressed: false
340 onRecognisedPressChanged: {
341 if (recognisedPress) {
347 if (status !== TouchGestureArea.Recognized) {
348 if (status === TouchGestureArea.WaitingForTouch) {
349 if (wasPressed && !dragging) {
350 launcher.openDrawer(true);
361 objectName: "inputMethod"
362 surface: shell.topLevelSurfaceList.inputMethodSurface
365 topMargin: panel.panelHeight
366 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
368 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
373 objectName: "greeterLoader"
375 anchors.topMargin: panel.panelHeight
377 if (shell.mode != "shell") {
378 if (screenWindow.primary) return integratedGreeter;
379 return secondaryGreeter;
381 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
384 item.objectName = "greeter"
386 property bool openDrawerAfterUnlock: false
390 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
391 launcher.openDrawer(false);
392 greeterLoader.openDrawerAfterUnlock = false;
399 id: integratedGreeter
402 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
403 hides: [launcher, panel.indicators, panel.applicationMenus]
404 tabletMode: shell.usageScenario != "phone"
405 forcedUnlock: wizard.active || shell.mode === "full-shell"
406 background: wallpaperResolver.cachedBackground
407 hasCustomBackground: wallpaperResolver.hasCustomBackground
408 allowFingerprint: !dialogs.hasActiveDialog &&
409 !notifications.topmostIsFullscreen &&
410 !panel.indicators.shown
412 // avoid overlapping with Launcher's edge drag area
413 // FIXME: Fix TouchRegistry & friends and remove this workaround
414 // Issue involves launcher's DDA getting disabled on a long
416 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
423 if (!tutorial.running) {
428 onEmergencyCall: startLockedApp("dialer-app")
435 hides: [launcher, panel.indicators]
440 // See powerConnection for why this is useful
441 id: showGreeterDelayed
444 // Go through the dbus service, because it has checks for whether
445 // we are even allowed to lock or not.
446 DBusUnitySessionService.PromptLock();
455 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
456 // We just received an incoming call while locked. The
457 // indicator will have already launched dialer-app for us, but
458 // there is a race between "hasCalls" changing and the dialer
459 // starting up. So in case we lose that race, we'll start/
460 // focus the dialer ourselves here too. Even if the indicator
461 // didn't launch the dialer for some reason (or maybe a call
462 // started via some other means), if an active call is
463 // happening, we want to be in the dialer.
464 startLockedApp("dialer-app")
474 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
475 !callManager.hasCalls && !wizard.active) {
476 // We don't want to simply call greeter.showNow() here, because
477 // that will take too long. Qt will delay button event
478 // handling until the greeter is done loading and may think the
479 // user held down the power button the whole time, leading to a
480 // power dialog being shown. Instead, delay showing the
481 // greeter until we've finished handling the event. We could
482 // make the greeter load asynchronously instead, but that
483 // introduces a whole host of timing issues, especially with
484 // its animations. So this is simpler.
485 showGreeterDelayed.start();
490 function showHome() {
491 greeter.notifyUserRequestedApp();
493 if (shell.mode === "greeter") {
494 SessionBroadcast.requestHomeShown(AccountsService.user);
496 if (!greeter.active) {
497 launcher.openDrawer(false);
499 greeterLoader.openDrawerAfterUnlock = true;
513 anchors.fill: parent //because this draws indicator menus
515 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
516 minimizedPanelHeight: units.gu(3)
517 expandedPanelHeight: units.gu(7)
518 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
519 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
520 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
524 available: tutorial.panelEnabled
525 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
526 && (!greeter || !greeter.hasLockedApp)
527 && !shell.waitingOnGreeter
528 && settings.enableIndicatorMenu
530 model: Indicators.IndicatorsModel {
531 // tablet and phone both use the same profile
532 // FIXME: use just "phone" for greeter too, but first fix
533 // greeter app launching to either load the app inside the
534 // greeter or tell the session to load the app. This will
535 // involve taking the url-dispatcher dbus name and using
536 // SessionBroadcast to tell the session.
537 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
538 Component.onCompleted: load();
544 available: (!greeter || !greeter.shown)
545 && !shell.waitingOnGreeter
546 && !stage.spreadShown
549 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
550 ? shell.topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
552 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
553 || greeter.hasLockedApp
554 greeterShown: greeter && greeter.shown
555 hasKeyboard: shell.hasKeyboard
556 panelState: panelState
561 objectName: "launcher"
563 anchors.top: parent.top
564 anchors.topMargin: inverted ? 0 : panel.panelHeight
565 anchors.bottom: parent.bottom
567 dragAreaWidth: shell.edgeSize
568 available: tutorial.launcherEnabled
569 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
570 && !greeter.hasLockedApp
571 && !shell.waitingOnGreeter
572 && settings.enableLauncher
573 inverted: shell.usageScenario !== "desktop"
574 superPressed: physicalKeysMapper.superPressed
575 superTabPressed: physicalKeysMapper.superTabPressed
576 panelWidth: units.gu(settings.launcherWidth)
577 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
578 blurSource: greeter.shown ? greeter : stages
579 topPanelHeight: panel.panelHeight
580 drawerEnabled: !greeter.active
581 privateMode: greeter.active
583 onShowDashHome: showHome()
584 onLauncherApplicationSelected: {
585 greeter.notifyUserRequestedApp();
586 shell.activateApplication(appId);
590 panel.indicators.hide();
591 panel.applicationMenus.hide();
594 onDrawerShownChanged: {
596 panel.indicators.hide();
597 panel.applicationMenus.hide();
607 shortcut: Qt.MetaModifier | Qt.Key_A
609 launcher.openDrawer(true);
613 shortcut: Qt.AltModifier | Qt.Key_F1
615 launcher.openForKeyboardNavigation();
619 shortcut: Qt.MetaModifier | Qt.Key_0
621 if (LauncherModel.get(9)) {
622 activateApplication(LauncherModel.get(9).appId);
629 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
631 if (LauncherModel.get(index)) {
632 activateApplication(LauncherModel.get(index).appId);
639 KeyboardShortcutsOverlay {
640 objectName: "shortcutsOverlay"
641 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
642 && height < parent.height - padding - panel.panelHeight
643 anchors.centerIn: parent
644 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
645 anchors.verticalCenterOffset: panel.panelHeight/2
647 opacity: enabled ? 0.95 : 0
649 Behavior on opacity {
650 UbuntuNumberAnimation {}
656 objectName: "tutorial"
659 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
660 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
661 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
662 inputMethod.visible ||
663 (launcher.shown && !launcher.lockedVisible) ||
664 panel.indicators.shown || stage.rightEdgeDragProgress > 0
665 usageScenario: shell.usageScenario
666 lastInputTimestamp: inputFilter.lastInputTimestamp
676 deferred: shell.mode === "greeter"
678 function unlockWhenDoneWithWizard() {
680 Connectivity.unlockAllModems();
684 Component.onCompleted: unlockWhenDoneWithWizard()
685 onActiveChanged: unlockWhenDoneWithWizard()
688 MouseArea { // modal notifications prevent interacting with other contents
690 visible: notifications.useModal
697 model: NotificationBackend.Model
699 hasMouse: shell.hasMouse
700 background: wallpaperResolver.cachedBackground
702 y: topmostIsFullscreen ? 0 : panel.panelHeight
703 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
708 when: overlay.width <= units.gu(60)
710 target: notifications
711 anchors.left: parent.left
712 anchors.right: parent.right
717 when: overlay.width > units.gu(60)
719 target: notifications
720 anchors.left: undefined
721 anchors.right: parent.right
723 PropertyChanges { target: notifications; width: units.gu(38) }
730 enabled: !greeter.shown
732 // NB: it does its own positioning according to the specified edge
736 panel.indicators.hide()
739 material: Component {
745 anchors.centerIn: parent
747 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
748 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
758 objectName: "dialogs"
760 visible: hasActiveDialog
762 usageScenario: shell.usageScenario
763 hasKeyboard: shell.hasKeyboard
765 shutdownFadeOutRectangle.enabled = true;
766 shutdownFadeOutRectangle.visible = true;
767 shutdownFadeOut.start();
772 target: SessionBroadcast
773 onShowHome: if (shell.mode !== "greeter") showHome()
778 objectName: "urlDispatcher"
779 active: shell.mode === "greeter"
780 onUrlRequested: shell.activateURL(url)
787 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
790 ignoreUnknownSignals: true
791 onItemSnapshotRequested: itemGrabber.capture(item)
796 id: cursorHidingTimer
798 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
799 onTriggered: cursor.opacity = 0;
804 objectName: "Pointer-"+screenWindow.objectName
808 topBoundaryOffset: panel.panelHeight
809 enabled: shell.hasMouse && screenWindow.active
812 property bool mouseNeverMoved: true
814 target: cursor; property: "x"; value: shell.width / 2
815 when: cursor.mouseNeverMoved && cursor.visible
818 target: cursor; property: "y"; value: shell.height / 2
819 when: cursor.mouseNeverMoved && cursor.visible
822 confiningItem: stage.itemConfiningMouseCursor
826 readonly property var previewRectangle: stage.previewRectangle.target &&
827 stage.previewRectangle.target.dragging ?
828 stage.previewRectangle : null
830 onPushedLeftBoundary: {
831 if (buttons === Qt.NoButton) {
832 launcher.pushEdge(amount);
833 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
834 previewRectangle.maximizeLeft(amount);
838 onPushedRightBoundary: {
839 if (buttons === Qt.NoButton) {
840 rightEdgeBarrier.push(amount);
841 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
842 previewRectangle.maximizeRight(amount);
846 onPushedTopBoundary: {
847 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
848 previewRectangle.maximize(amount);
851 onPushedTopLeftCorner: {
852 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
853 previewRectangle.maximizeTopLeft(amount);
856 onPushedTopRightCorner: {
857 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
858 previewRectangle.maximizeTopRight(amount);
861 onPushedBottomLeftCorner: {
862 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
863 previewRectangle.maximizeBottomLeft(amount);
866 onPushedBottomRightCorner: {
867 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
868 previewRectangle.maximizeBottomRight(amount);
872 if (previewRectangle) {
873 previewRectangle.stop();
878 mouseNeverMoved = false;
882 Behavior on opacity { UbuntuNumberAnimation {} }
885 // non-visual objects
887 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
892 id: shutdownFadeOutRectangle
899 NumberAnimation on opacity {
904 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
905 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