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
75 // to be read from outside
76 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
78 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
79 && stage.orientationChangesEnabled
80 && (!greeter || !greeter.animating)
82 readonly property bool showingGreeter: greeter && greeter.shown
84 property bool startingUp: true
85 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
87 property int supportedOrientations: {
89 // Ensure we don't rotate during start up
90 return Qt.PrimaryOrientation;
91 } else if (showingGreeter || notifications.topmostIsFullscreen) {
92 return Qt.PrimaryOrientation;
94 return shell.orientations.map(stage.supportedOrientations);
98 readonly property var mainApp: stage.mainApp
102 _onMainAppChanged(mainApp.appId);
106 target: ApplicationManager
108 if (shell.mainApp && shell.mainApp.appId === appId) {
109 _onMainAppChanged(appId);
113 function _onMainAppChanged(appId) {
114 if (wizard.active && appId != "" && appId != "unity8-dash") {
115 // If this happens on first boot, we may be in the
116 // wizard while receiving a call. But a call is more
117 // important than the wizard so just bail out of it.
121 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
122 // If we are in the middle of a call, make dialer lockedApp and show it.
123 // This can happen if user backs out of dialer back to greeter, then
124 // launches dialer again.
125 greeter.lockedApp = appId;
127 greeter.notifyAppFocusRequested(appId);
129 panel.indicators.hide();
130 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
133 // For autopilot consumption
134 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
136 // Note when greeter is waiting on PAM, so that we can disable edges until
137 // we know which user data to show and whether the session is locked.
138 readonly property bool waitingOnGreeter: greeter && greeter.waiting
140 property real edgeSize: units.gu(settings.edgeDragWidth)
143 id: wallpaperResolver
144 objectName: "wallpaperResolver"
146 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
147 readonly property bool hasCustomBackground: background != defaultBackground
149 // Use a cached version of the scaled-down wallpaper (as sometimes the
150 // image can be quite big compared to the device size, including for
151 // our default wallpaper). We use a name=wallpaper argument here to
152 // make sure we don't litter our cache with lots of scaled images. We
153 // only need to bother caching one at a time.
154 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
157 id: backgroundSettings
158 schema.id: "org.gnome.desktop.background"
162 AccountsService.backgroundFile,
163 backgroundSettings.pictureUri,
168 readonly property alias greeter: greeterLoader.item
170 function activateApplication(appId) {
171 // Either open the app in our own session, or -- if we're acting as a
172 // greeter -- ask the user's session to open it for us.
173 if (shell.mode === "greeter") {
174 activateURL("application:///" + appId + ".desktop");
180 function activateURL(url) {
181 SessionBroadcast.requestUrlStart(AccountsService.user, url);
182 greeter.notifyUserRequestedApp();
183 panel.indicators.hide();
186 function startApp(appId) {
187 if (ApplicationManager.findApplication(appId)) {
188 ApplicationManager.requestFocusApplication(appId);
190 print("calling startApplication. active workspace is", WorkspaceManager.activeWorkspace)
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
280 surfaceManager: SurfaceManager
281 workspace: screenWindow.screen.currentWorkspace
290 dragAreaWidth: shell.edgeSize
291 background: wallpaperResolver.background
293 applicationManager: ApplicationManager
294 topLevelSurfaceList: topLevelSurfaceList
295 inputMethodRect: inputMethod.visibleRect
296 rightEdgePushProgress: rightEdgeBarrier.progress
298 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
300 : shell.usageScenario
302 mode: usageScenario == "phone" ? "staged"
303 : usageScenario == "tablet" ? "stagedWithSideStage"
306 shellOrientation: shell.orientation
307 shellOrientationAngle: shell.orientationAngle
308 orientations: shell.orientations
309 nativeWidth: shell.nativeWidth
310 nativeHeight: shell.nativeHeight
312 interactive: (!greeter || !greeter.shown)
313 && panel.indicators.fullyClosed
314 && !notifications.useModal
316 onInteractiveChanged: { if (interactive) { focus = true; } }
318 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
319 suspended: greeter.shown
320 altTabPressed: physicalKeysMapper.altTabPressed
321 oskEnabled: shell.oskEnabled
322 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
323 panelState: panelState
325 onSpreadShownChanged: {
326 panel.indicators.hide();
327 panel.applicationMenus.hide();
334 minimumTouchPoints: 4
335 maximumTouchPoints: minimumTouchPoints
337 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
338 touchPoints.length >= minimumTouchPoints &&
339 touchPoints.length <= maximumTouchPoints
340 property bool wasPressed: false
342 onRecognisedPressChanged: {
343 if (recognisedPress) {
349 if (status !== TouchGestureArea.Recognized) {
350 if (status === TouchGestureArea.WaitingForTouch) {
351 if (wasPressed && !dragging) {
352 launcher.openDrawer(true);
363 objectName: "inputMethod"
364 surface: topLevelSurfaceList.inputMethodSurface
367 topMargin: panel.panelHeight
368 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
370 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
375 objectName: "greeterLoader"
377 anchors.topMargin: panel.panelHeight
379 if (shell.mode != "shell") {
380 if (screenWindow.primary) return integratedGreeter;
381 return secondaryGreeter;
383 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
386 item.objectName = "greeter"
388 property bool openDrawerAfterUnlock: false
392 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
393 launcher.openDrawer(false);
394 greeterLoader.openDrawerAfterUnlock = false;
401 id: integratedGreeter
404 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
405 hides: [launcher, panel.indicators, panel.applicationMenus]
406 tabletMode: shell.usageScenario != "phone"
407 forcedUnlock: wizard.active || shell.mode === "full-shell"
408 background: wallpaperResolver.cachedBackground
409 hasCustomBackground: wallpaperResolver.hasCustomBackground
410 allowFingerprint: !dialogs.hasActiveDialog &&
411 !notifications.topmostIsFullscreen &&
412 !panel.indicators.shown
414 // avoid overlapping with Launcher's edge drag area
415 // FIXME: Fix TouchRegistry & friends and remove this workaround
416 // Issue involves launcher's DDA getting disabled on a long
418 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
425 if (!tutorial.running) {
430 onEmergencyCall: startLockedApp("dialer-app")
437 hides: [launcher, panel.indicators]
442 // See powerConnection for why this is useful
443 id: showGreeterDelayed
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
523 available: tutorial.panelEnabled
524 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
525 && (!greeter || !greeter.hasLockedApp)
526 && !shell.waitingOnGreeter
527 && settings.enableIndicatorMenu
529 model: Indicators.IndicatorsModel {
530 // tablet and phone both use the same profile
531 // FIXME: use just "phone" for greeter too, but first fix
532 // greeter app launching to either load the app inside the
533 // greeter or tell the session to load the app. This will
534 // involve taking the url-dispatcher dbus name and using
535 // SessionBroadcast to tell the session.
536 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
537 Component.onCompleted: load();
543 available: (!greeter || !greeter.shown)
544 && !shell.waitingOnGreeter
545 && !stage.spreadShown
548 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
549 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
551 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
552 || greeter.hasLockedApp
553 greeterShown: greeter && greeter.shown
554 panelState: panelState
559 objectName: "launcher"
561 anchors.top: parent.top
562 anchors.topMargin: inverted ? 0 : panel.panelHeight
563 anchors.bottom: parent.bottom
565 dragAreaWidth: shell.edgeSize
566 available: tutorial.launcherEnabled
567 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
568 && !greeter.hasLockedApp
569 && !shell.waitingOnGreeter
570 && settings.enableLauncher
571 inverted: shell.usageScenario !== "desktop"
572 superPressed: physicalKeysMapper.superPressed
573 superTabPressed: physicalKeysMapper.superTabPressed
574 panelWidth: units.gu(settings.launcherWidth)
575 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
576 blurSource: greeter.shown ? greeter : stages
577 topPanelHeight: panel.panelHeight
578 drawerEnabled: !greeter.active
580 onShowDashHome: showHome()
581 onLauncherApplicationSelected: {
582 greeter.notifyUserRequestedApp();
583 shell.activateApplication(appId);
587 panel.indicators.hide()
597 shortcut: Qt.MetaModifier | Qt.Key_A
599 launcher.openDrawer(true);
603 shortcut: Qt.AltModifier | Qt.Key_F1
605 launcher.openForKeyboardNavigation();
609 shortcut: Qt.MetaModifier | Qt.Key_0
611 if (LauncherModel.get(9)) {
612 activateApplication(LauncherModel.get(9).appId);
619 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
621 if (LauncherModel.get(index)) {
622 activateApplication(LauncherModel.get(index).appId);
629 KeyboardShortcutsOverlay {
630 objectName: "shortcutsOverlay"
631 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
632 && height < parent.height - padding - panel.panelHeight
633 anchors.centerIn: parent
634 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
635 anchors.verticalCenterOffset: panel.panelHeight/2
637 opacity: enabled ? 0.95 : 0
639 Behavior on opacity {
640 UbuntuNumberAnimation {}
646 objectName: "tutorial"
649 paused: callManager.hasCalls || !greeter || greeter.active ||
651 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
652 inputMethod.visible ||
653 (launcher.shown && !launcher.lockedVisible) ||
654 panel.indicators.shown || stage.rightEdgeDragProgress > 0
655 usageScenario: shell.usageScenario
656 lastInputTimestamp: inputFilter.lastInputTimestamp
666 deferred: shell.mode === "greeter"
668 function unlockWhenDoneWithWizard() {
670 Connectivity.unlockAllModems();
674 Component.onCompleted: unlockWhenDoneWithWizard()
675 onActiveChanged: unlockWhenDoneWithWizard()
678 MouseArea { // modal notifications prevent interacting with other contents
680 visible: notifications.useModal
687 model: NotificationBackend.Model
689 hasMouse: shell.hasMouse
690 background: wallpaperResolver.cachedBackground
692 y: topmostIsFullscreen ? 0 : panel.panelHeight
693 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
698 when: overlay.width <= units.gu(60)
700 target: notifications
701 anchors.left: parent.left
702 anchors.right: parent.right
707 when: overlay.width > units.gu(60)
709 target: notifications
710 anchors.left: undefined
711 anchors.right: parent.right
713 PropertyChanges { target: notifications; width: units.gu(38) }
720 enabled: !greeter.shown
722 // NB: it does its own positioning according to the specified edge
726 panel.indicators.hide()
729 material: Component {
735 anchors.centerIn: parent
737 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
738 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
748 objectName: "dialogs"
750 visible: hasActiveDialog
752 usageScenario: shell.usageScenario
753 hasKeyboard: shell.hasKeyboard
755 shutdownFadeOutRectangle.enabled = true;
756 shutdownFadeOutRectangle.visible = true;
757 shutdownFadeOut.start();
762 target: SessionBroadcast
763 onShowHome: if (shell.mode !== "greeter") showHome()
768 objectName: "urlDispatcher"
769 active: shell.mode === "greeter"
770 onUrlRequested: shell.activateURL(url)
777 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
780 ignoreUnknownSignals: true
781 onItemSnapshotRequested: itemGrabber.capture(item)
786 id: cursorHidingTimer
788 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
789 onTriggered: cursor.opacity = 0;
794 objectName: "Pointer-"+screenWindow.objectName
798 topBoundaryOffset: panel.panelHeight
799 enabled: shell.hasMouse && screenWindow.active
802 property bool mouseNeverMoved: true
804 target: cursor; property: "x"; value: shell.width / 2
805 when: cursor.mouseNeverMoved && cursor.visible
808 target: cursor; property: "y"; value: shell.height / 2
809 when: cursor.mouseNeverMoved && cursor.visible
812 confiningItem: stage.itemConfiningMouseCursor
816 readonly property var previewRectangle: stage.previewRectangle.target &&
817 stage.previewRectangle.target.dragging ?
818 stage.previewRectangle : null
820 onPushedLeftBoundary: {
821 if (buttons === Qt.NoButton) {
822 launcher.pushEdge(amount);
823 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
824 previewRectangle.maximizeLeft(amount);
828 onPushedRightBoundary: {
829 if (buttons === Qt.NoButton) {
830 rightEdgeBarrier.push(amount);
831 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
832 previewRectangle.maximizeRight(amount);
836 onPushedTopBoundary: {
837 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
838 previewRectangle.maximize(amount);
841 onPushedTopLeftCorner: {
842 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
843 previewRectangle.maximizeTopLeft(amount);
846 onPushedTopRightCorner: {
847 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
848 previewRectangle.maximizeTopRight(amount);
851 onPushedBottomLeftCorner: {
852 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
853 previewRectangle.maximizeBottomLeft(amount);
856 onPushedBottomRightCorner: {
857 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
858 previewRectangle.maximizeBottomRight(amount);
862 if (previewRectangle) {
863 previewRectangle.stop();
868 mouseNeverMoved = false;
872 Behavior on opacity { UbuntuNumberAnimation {} }
875 // non-visual objects
877 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
882 id: shutdownFadeOutRectangle
889 NumberAnimation on opacity {
894 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
895 DBusUnitySessionService.shutdown();
902 z: shutdownFadeOutRectangle.z + 1
903 active: DebuggingController.logOverlay
905 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