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 alias surfaceManager: topLevelSurfaceList.surfaceManager
58 property int orientationAngle: 0
59 property int orientation
60 property Orientations orientations
61 property real nativeWidth
62 property real nativeHeight
63 property alias panelAreaShowProgress: panel.panelAreaShowProgress
64 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
65 property string mode: "full-greeter"
66 property alias oskEnabled: inputMethod.enabled
67 function updateFocusedAppOrientation() {
68 stage.updateFocusedAppOrientation();
70 function updateFocusedAppOrientationAnimated() {
71 stage.updateFocusedAppOrientationAnimated();
73 property bool hasMouse: false
74 property bool hasKeyboard: 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
103 _onMainAppChanged(mainApp.appId);
107 target: ApplicationManager
109 if (shell.mainApp && shell.mainApp.appId === appId) {
110 _onMainAppChanged(appId);
114 function _onMainAppChanged(appId) {
115 if (wizard.active && appId != "" && appId != "unity8-dash") {
116 // If this happens on first boot, we may be in the
117 // wizard while receiving a call. But a call is more
118 // important than the wizard so just bail out of it.
122 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
123 // If we are in the middle of a call, make dialer lockedApp and show it.
124 // This can happen if user backs out of dialer back to greeter, then
125 // launches dialer again.
126 greeter.lockedApp = appId;
128 greeter.notifyAppFocusRequested(appId);
130 panel.indicators.hide();
131 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
134 // For autopilot consumption
135 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
137 // Note when greeter is waiting on PAM, so that we can disable edges until
138 // we know which user data to show and whether the session is locked.
139 readonly property bool waitingOnGreeter: greeter && greeter.waiting
141 property real edgeSize: units.gu(settings.edgeDragWidth)
144 id: wallpaperResolver
145 objectName: "wallpaperResolver"
147 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
148 readonly property bool hasCustomBackground: background != defaultBackground
150 // Use a cached version of the scaled-down wallpaper (as sometimes the
151 // image can be quite big compared to the device size, including for
152 // our default wallpaper). We use a name=wallpaper argument here to
153 // make sure we don't litter our cache with lots of scaled images. We
154 // only need to bother caching one at a time.
155 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
158 id: backgroundSettings
159 schema.id: "org.gnome.desktop.background"
163 AccountsService.backgroundFile,
164 backgroundSettings.pictureUri,
169 readonly property alias greeter: greeterLoader.item
171 function activateApplication(appId) {
172 // Either open the app in our own session, or -- if we're acting as a
173 // greeter -- ask the user's session to open it for us.
174 if (shell.mode === "greeter") {
175 activateURL("application:///" + appId + ".desktop");
181 function activateURL(url) {
182 SessionBroadcast.requestUrlStart(AccountsService.user, url);
183 greeter.notifyUserRequestedApp();
184 panel.indicators.hide();
187 function startApp(appId) {
188 if (ApplicationManager.findApplication(appId)) {
189 ApplicationManager.requestFocusApplication(appId);
191 ApplicationManager.startApplication(appId);
195 function startLockedApp(app) {
196 if (greeter.locked) {
197 greeter.lockedApp = app;
199 startApp(app); // locked apps are always in our same session
203 target: LauncherModel
204 property: "applicationManager"
205 value: ApplicationManager
208 Component.onCompleted: {
209 finishStartUpTimer.start();
218 objectName: "dashCommunicator"
222 id: physicalKeysMapper
223 objectName: "physicalKeysMapper"
225 onPowerKeyLongPressed: dialogs.showPowerDialog();
226 onVolumeDownTriggered: volumeControl.volumeDown();
227 onVolumeUpTriggered: volumeControl.volumeUp();
228 onScreenshotTriggered: itemGrabber.capture(shell);
232 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
237 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
238 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
242 objectName: "windowInputMonitor"
243 onHomeKeyActivated: {
244 // Ignore when greeter is active, to avoid pocket presses
245 if (!greeter.active) {
247 ApplicationManager.requestFocusApplication("unity8-dash");
250 onTouchBegun: { cursor.opacity = 0; }
252 // move the (hidden) cursor to the last known touch position
253 var mappedCoords = mapFromItem(null, pos.x, pos.y);
254 cursor.x = mappedCoords.x;
255 cursor.y = mappedCoords.y;
256 cursor.mouseNeverMoved = false;
262 schema.id: "com.canonical.Unity8"
267 objectName: "panelState"
274 height: parent.height
276 TopLevelWindowModel {
277 id: topLevelSurfaceList
278 objectName: "topLevelSurfaceList"
279 applicationManager: ApplicationManager // it's a singleton
288 dragAreaWidth: shell.edgeSize
289 background: wallpaperResolver.background
291 applicationManager: ApplicationManager
292 topLevelSurfaceList: 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: 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
453 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
454 // We just received an incoming call while locked. The
455 // indicator will have already launched dialer-app for us, but
456 // there is a race between "hasCalls" changing and the dialer
457 // starting up. So in case we lose that race, we'll start/
458 // focus the dialer ourselves here too. Even if the indicator
459 // didn't launch the dialer for some reason (or maybe a call
460 // started via some other means), if an active call is
461 // happening, we want to be in the dialer.
462 startLockedApp("dialer-app")
472 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
473 !callManager.hasCalls && !wizard.active) {
474 // We don't want to simply call greeter.showNow() here, because
475 // that will take too long. Qt will delay button event
476 // handling until the greeter is done loading and may think the
477 // user held down the power button the whole time, leading to a
478 // power dialog being shown. Instead, delay showing the
479 // greeter until we've finished handling the event. We could
480 // make the greeter load asynchronously instead, but that
481 // introduces a whole host of timing issues, especially with
482 // its animations. So this is simpler.
483 showGreeterDelayed.start();
488 function showHome() {
489 greeter.notifyUserRequestedApp();
491 if (shell.mode === "greeter") {
492 SessionBroadcast.requestHomeShown(AccountsService.user);
494 if (!greeter.active) {
495 launcher.openDrawer(false);
497 greeterLoader.openDrawerAfterUnlock = true;
511 anchors.fill: parent //because this draws indicator menus
513 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
514 minimizedPanelHeight: units.gu(3)
515 expandedPanelHeight: units.gu(7)
516 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
517 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
521 available: tutorial.panelEnabled
522 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
523 && (!greeter || !greeter.hasLockedApp)
524 && !shell.waitingOnGreeter
525 && settings.enableIndicatorMenu
527 model: Indicators.IndicatorsModel {
528 // tablet and phone both use the same profile
529 // FIXME: use just "phone" for greeter too, but first fix
530 // greeter app launching to either load the app inside the
531 // greeter or tell the session to load the app. This will
532 // involve taking the url-dispatcher dbus name and using
533 // SessionBroadcast to tell the session.
534 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
535 Component.onCompleted: load();
541 available: (!greeter || !greeter.shown)
542 && !shell.waitingOnGreeter
543 && !stage.spreadShown
546 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
547 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
549 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
550 || greeter.hasLockedApp
551 greeterShown: greeter && greeter.shown
552 panelState: panelState
557 objectName: "launcher"
559 anchors.top: parent.top
560 anchors.topMargin: inverted ? 0 : panel.panelHeight
561 anchors.bottom: parent.bottom
563 dragAreaWidth: shell.edgeSize
564 available: tutorial.launcherEnabled
565 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
566 && !greeter.hasLockedApp
567 && !shell.waitingOnGreeter
568 && settings.enableLauncher
569 inverted: shell.usageScenario !== "desktop"
570 superPressed: physicalKeysMapper.superPressed
571 superTabPressed: physicalKeysMapper.superTabPressed
572 panelWidth: units.gu(settings.launcherWidth)
573 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
574 blurSource: greeter.shown ? greeter : stages
575 topPanelHeight: panel.panelHeight
576 drawerEnabled: !greeter.active
578 onShowDashHome: showHome()
579 onLauncherApplicationSelected: {
580 greeter.notifyUserRequestedApp();
581 shell.activateApplication(appId);
585 panel.indicators.hide()
595 shortcut: Qt.MetaModifier | Qt.Key_A
597 launcher.openDrawer(true);
601 shortcut: Qt.AltModifier | Qt.Key_F1
603 launcher.openForKeyboardNavigation();
607 shortcut: Qt.MetaModifier | Qt.Key_0
609 if (LauncherModel.get(9)) {
610 activateApplication(LauncherModel.get(9).appId);
617 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
619 if (LauncherModel.get(index)) {
620 activateApplication(LauncherModel.get(index).appId);
627 KeyboardShortcutsOverlay {
628 objectName: "shortcutsOverlay"
629 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
630 && height < parent.height - padding - panel.panelHeight
631 anchors.centerIn: parent
632 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
633 anchors.verticalCenterOffset: panel.panelHeight/2
635 opacity: enabled ? 0.95 : 0
637 Behavior on opacity {
638 UbuntuNumberAnimation {}
644 objectName: "tutorial"
647 paused: callManager.hasCalls || !greeter || greeter.active ||
649 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
650 inputMethod.visible ||
651 (launcher.shown && !launcher.lockedVisible) ||
652 panel.indicators.shown || stage.rightEdgeDragProgress > 0
653 usageScenario: shell.usageScenario
654 lastInputTimestamp: inputFilter.lastInputTimestamp
664 deferred: shell.mode === "greeter"
666 function unlockWhenDoneWithWizard() {
668 Connectivity.unlockAllModems();
672 Component.onCompleted: unlockWhenDoneWithWizard()
673 onActiveChanged: unlockWhenDoneWithWizard()
676 MouseArea { // modal notifications prevent interacting with other contents
678 visible: notifications.useModal
685 model: NotificationBackend.Model
687 hasMouse: shell.hasMouse
688 background: wallpaperResolver.cachedBackground
690 y: topmostIsFullscreen ? 0 : panel.panelHeight
691 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
696 when: overlay.width <= units.gu(60)
698 target: notifications
699 anchors.left: parent.left
700 anchors.right: parent.right
705 when: overlay.width > units.gu(60)
707 target: notifications
708 anchors.left: undefined
709 anchors.right: parent.right
711 PropertyChanges { target: notifications; width: units.gu(38) }
718 enabled: !greeter.shown
720 // NB: it does its own positioning according to the specified edge
724 panel.indicators.hide()
727 material: Component {
733 anchors.centerIn: parent
735 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
736 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
746 objectName: "dialogs"
748 visible: hasActiveDialog
750 usageScenario: shell.usageScenario
751 hasKeyboard: shell.hasKeyboard
753 shutdownFadeOutRectangle.enabled = true;
754 shutdownFadeOutRectangle.visible = true;
755 shutdownFadeOut.start();
760 target: SessionBroadcast
761 onShowHome: if (shell.mode !== "greeter") showHome()
766 objectName: "urlDispatcher"
767 active: shell.mode === "greeter"
768 onUrlRequested: shell.activateURL(url)
775 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
778 ignoreUnknownSignals: true
779 onItemSnapshotRequested: itemGrabber.capture(item)
784 id: cursorHidingTimer
786 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
787 onTriggered: cursor.opacity = 0;
792 objectName: "Pointer-"+screenWindow.objectName
796 topBoundaryOffset: panel.panelHeight
797 enabled: shell.hasMouse && screenWindow.active
800 property bool mouseNeverMoved: true
802 target: cursor; property: "x"; value: shell.width / 2
803 when: cursor.mouseNeverMoved && cursor.visible
806 target: cursor; property: "y"; value: shell.height / 2
807 when: cursor.mouseNeverMoved && cursor.visible
810 confiningItem: stage.itemConfiningMouseCursor
814 readonly property var previewRectangle: stage.previewRectangle.target &&
815 stage.previewRectangle.target.dragging ?
816 stage.previewRectangle : null
818 onPushedLeftBoundary: {
819 if (buttons === Qt.NoButton) {
820 launcher.pushEdge(amount);
821 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
822 previewRectangle.maximizeLeft(amount);
826 onPushedRightBoundary: {
827 if (buttons === Qt.NoButton) {
828 rightEdgeBarrier.push(amount);
829 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
830 previewRectangle.maximizeRight(amount);
834 onPushedTopBoundary: {
835 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
836 previewRectangle.maximize(amount);
839 onPushedTopLeftCorner: {
840 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
841 previewRectangle.maximizeTopLeft(amount);
844 onPushedTopRightCorner: {
845 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
846 previewRectangle.maximizeTopRight(amount);
849 onPushedBottomLeftCorner: {
850 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
851 previewRectangle.maximizeBottomLeft(amount);
854 onPushedBottomRightCorner: {
855 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
856 previewRectangle.maximizeBottomRight(amount);
860 if (previewRectangle) {
861 previewRectangle.stop();
866 mouseNeverMoved = false;
870 Behavior on opacity { UbuntuNumberAnimation {} }
873 // non-visual objects
875 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
880 id: shutdownFadeOutRectangle
887 NumberAnimation on opacity {
892 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
893 DBusUnitySessionService.shutdown();
900 z: shutdownFadeOutRectangle.z + 1
901 active: DebuggingController.logOverlay
903 sourceComponent: Text {
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