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
72 property bool hasTouchscreen: false
74 // to be read from outside
75 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
77 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78 && stage.orientationChangesEnabled
79 && (!greeter || !greeter.animating)
81 readonly property bool showingGreeter: greeter && greeter.shown
83 property bool startingUp: true
84 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
86 property int supportedOrientations: {
88 // Ensure we don't rotate during start up
89 return Qt.PrimaryOrientation;
90 } else if (showingGreeter || notifications.topmostIsFullscreen) {
91 return Qt.PrimaryOrientation;
93 return shell.orientations.map(stage.supportedOrientations);
97 readonly property var mainApp: stage.mainApp
101 _onMainAppChanged(mainApp.appId);
105 target: ApplicationManager
107 if (shell.mainApp && shell.mainApp.appId === appId) {
108 _onMainAppChanged(appId);
112 function _onMainAppChanged(appId) {
113 if (wizard.active && appId != "" && appId != "unity8-dash") {
114 // If this happens on first boot, we may be in the
115 // wizard while receiving a call. But a call is more
116 // important than the wizard so just bail out of it.
120 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
121 // If we are in the middle of a call, make dialer lockedApp and show it.
122 // This can happen if user backs out of dialer back to greeter, then
123 // launches dialer again.
124 greeter.lockedApp = appId;
126 greeter.notifyAppFocusRequested(appId);
128 panel.indicators.hide();
129 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
132 // For autopilot consumption
133 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
135 // Note when greeter is waiting on PAM, so that we can disable edges until
136 // we know which user data to show and whether the session is locked.
137 readonly property bool waitingOnGreeter: greeter && greeter.waiting
139 property real edgeSize: units.gu(settings.edgeDragWidth)
142 id: wallpaperResolver
143 objectName: "wallpaperResolver"
145 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
146 readonly property bool hasCustomBackground: background != defaultBackground
148 // Use a cached version of the scaled-down wallpaper (as sometimes the
149 // image can be quite big compared to the device size, including for
150 // our default wallpaper). We use a name=wallpaper argument here to
151 // make sure we don't litter our cache with lots of scaled images. We
152 // only need to bother caching one at a time.
153 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
156 id: backgroundSettings
157 schema.id: "org.gnome.desktop.background"
161 AccountsService.backgroundFile,
162 backgroundSettings.pictureUri,
167 readonly property alias greeter: greeterLoader.item
169 function activateApplication(appId) {
170 // Either open the app in our own session, or -- if we're acting as a
171 // greeter -- ask the user's session to open it for us.
172 if (shell.mode === "greeter") {
173 activateURL("application:///" + appId + ".desktop");
179 function activateURL(url) {
180 SessionBroadcast.requestUrlStart(AccountsService.user, url);
181 greeter.notifyUserRequestedApp();
182 panel.indicators.hide();
185 function startApp(appId) {
186 if (ApplicationManager.findApplication(appId)) {
187 ApplicationManager.requestFocusApplication(appId);
189 ApplicationManager.startApplication(appId);
193 function startLockedApp(app) {
194 if (greeter.locked) {
195 greeter.lockedApp = app;
197 startApp(app); // locked apps are always in our same session
201 target: LauncherModel
202 property: "applicationManager"
203 value: ApplicationManager
206 Component.onCompleted: {
207 finishStartUpTimer.start();
216 objectName: "dashCommunicator"
220 id: physicalKeysMapper
221 objectName: "physicalKeysMapper"
223 onPowerKeyLongPressed: dialogs.showPowerDialog();
224 onVolumeDownTriggered: volumeControl.volumeDown();
225 onVolumeUpTriggered: volumeControl.volumeUp();
226 onScreenshotTriggered: itemGrabber.capture(shell);
230 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
235 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
236 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
240 objectName: "windowInputMonitor"
241 onHomeKeyActivated: {
242 // Ignore when greeter is active, to avoid pocket presses
243 if (!greeter.active) {
245 ApplicationManager.requestFocusApplication("unity8-dash");
248 onTouchBegun: { cursor.opacity = 0; }
250 // move the (hidden) cursor to the last known touch position
251 var mappedCoords = mapFromItem(null, pos.x, pos.y);
252 cursor.x = mappedCoords.x;
253 cursor.y = mappedCoords.y;
254 cursor.mouseNeverMoved = false;
260 schema.id: "com.canonical.Unity8"
267 height: parent.height
271 objectName: "surfaceManager"
273 TopLevelWindowModel {
274 id: topLevelSurfaceList
275 objectName: "topLevelSurfaceList"
276 applicationManager: ApplicationManager // it's a singleton
277 surfaceManager: surfaceMan
286 dragAreaWidth: shell.edgeSize
287 background: wallpaperResolver.background
289 applicationManager: ApplicationManager
290 topLevelSurfaceList: topLevelSurfaceList
291 inputMethodRect: inputMethod.visibleRect
292 rightEdgePushProgress: rightEdgeBarrier.progress
294 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
296 : shell.usageScenario
298 mode: usageScenario == "phone" ? "staged"
299 : usageScenario == "tablet" ? "stagedWithSideStage"
302 shellOrientation: shell.orientation
303 shellOrientationAngle: shell.orientationAngle
304 orientations: shell.orientations
305 nativeWidth: shell.nativeWidth
306 nativeHeight: shell.nativeHeight
308 interactive: (!greeter || !greeter.shown)
309 && panel.indicators.fullyClosed
310 && !notifications.useModal
312 onInteractiveChanged: { if (interactive) { focus = true; } }
314 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
315 suspended: greeter.shown
316 altTabPressed: physicalKeysMapper.altTabPressed
317 oskEnabled: shell.oskEnabled
318 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
320 onSpreadShownChanged: {
321 panel.indicators.hide();
322 panel.applicationMenus.hide();
329 minimumTouchPoints: 4
330 maximumTouchPoints: minimumTouchPoints
332 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
333 touchPoints.length >= minimumTouchPoints &&
334 touchPoints.length <= maximumTouchPoints
335 property bool wasPressed: false
337 onRecognisedPressChanged: {
338 if (recognisedPress) {
344 if (status !== TouchGestureArea.Recognized) {
345 if (status === TouchGestureArea.WaitingForTouch) {
346 if (wasPressed && !dragging) {
347 launcher.openDrawer(true);
358 objectName: "inputMethod"
359 surface: topLevelSurfaceList.inputMethodSurface
362 topMargin: panel.panelHeight
363 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
365 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
370 objectName: "greeterLoader"
372 anchors.topMargin: panel.panelHeight
373 sourceComponent: shell.mode != "shell" ? integratedGreeter :
374 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
376 item.objectName = "greeter"
378 property bool openDrawerAfterUnlock: false
382 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
383 launcher.openDrawer(false);
384 greeterLoader.openDrawerAfterUnlock = false;
391 id: integratedGreeter
394 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
395 hides: [launcher, panel.indicators, panel.applicationMenus]
396 tabletMode: shell.usageScenario != "phone"
397 forcedUnlock: wizard.active || shell.mode === "full-shell"
398 background: wallpaperResolver.cachedBackground
399 hasCustomBackground: wallpaperResolver.hasCustomBackground
400 allowFingerprint: !dialogs.hasActiveDialog &&
401 !notifications.topmostIsFullscreen &&
402 !panel.indicators.shown
404 // avoid overlapping with Launcher's edge drag area
405 // FIXME: Fix TouchRegistry & friends and remove this workaround
406 // Issue involves launcher's DDA getting disabled on a long
408 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
415 if (!tutorial.running) {
420 onEmergencyCall: startLockedApp("dialer-app")
425 // See powerConnection for why this is useful
426 id: showGreeterDelayed
429 // Go through the dbus service, because it has checks for whether
430 // we are even allowed to lock or not.
431 DBusUnitySessionService.PromptLock();
440 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
441 // We just received an incoming call while locked. The
442 // indicator will have already launched dialer-app for us, but
443 // there is a race between "hasCalls" changing and the dialer
444 // starting up. So in case we lose that race, we'll start/
445 // focus the dialer ourselves here too. Even if the indicator
446 // didn't launch the dialer for some reason (or maybe a call
447 // started via some other means), if an active call is
448 // happening, we want to be in the dialer.
449 startLockedApp("dialer-app")
459 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
460 !callManager.hasCalls && !wizard.active) {
461 // We don't want to simply call greeter.showNow() here, because
462 // that will take too long. Qt will delay button event
463 // handling until the greeter is done loading and may think the
464 // user held down the power button the whole time, leading to a
465 // power dialog being shown. Instead, delay showing the
466 // greeter until we've finished handling the event. We could
467 // make the greeter load asynchronously instead, but that
468 // introduces a whole host of timing issues, especially with
469 // its animations. So this is simpler.
470 showGreeterDelayed.start();
475 function showHome() {
476 greeter.notifyUserRequestedApp();
478 if (shell.mode === "greeter") {
479 SessionBroadcast.requestHomeShown(AccountsService.user);
481 if (!greeter.active) {
482 launcher.openDrawer(false);
484 greeterLoader.openDrawerAfterUnlock = true;
498 anchors.fill: parent //because this draws indicator menus
500 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
501 minimizedPanelHeight: units.gu(3)
502 expandedPanelHeight: units.gu(7)
503 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
504 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
508 available: tutorial.panelEnabled
509 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
510 && (!greeter || !greeter.hasLockedApp)
511 && !shell.waitingOnGreeter
512 && settings.enableIndicatorMenu
514 model: Indicators.IndicatorsModel {
515 // tablet and phone both use the same profile
516 // FIXME: use just "phone" for greeter too, but first fix
517 // greeter app launching to either load the app inside the
518 // greeter or tell the session to load the app. This will
519 // involve taking the url-dispatcher dbus name and using
520 // SessionBroadcast to tell the session.
521 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
522 Component.onCompleted: load();
528 available: (!greeter || !greeter.shown)
529 && !shell.waitingOnGreeter
530 && !stage.spreadShown
533 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
534 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
536 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
537 || greeter.hasLockedApp
538 greeterShown: greeter && greeter.shown
539 hasKeyboard: shell.hasKeyboard
544 objectName: "launcher"
546 anchors.top: parent.top
547 anchors.topMargin: inverted ? 0 : panel.panelHeight
548 anchors.bottom: parent.bottom
550 dragAreaWidth: shell.edgeSize
551 available: tutorial.launcherEnabled
552 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
553 && !greeter.hasLockedApp
554 && !shell.waitingOnGreeter
555 && settings.enableLauncher
556 inverted: shell.usageScenario !== "desktop"
557 superPressed: physicalKeysMapper.superPressed
558 superTabPressed: physicalKeysMapper.superTabPressed
559 panelWidth: units.gu(settings.launcherWidth)
560 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
561 blurSource: greeter.shown ? greeter : stages
562 topPanelHeight: panel.panelHeight
563 drawerEnabled: !greeter.active
565 onShowDashHome: showHome()
566 onLauncherApplicationSelected: {
567 greeter.notifyUserRequestedApp();
568 shell.activateApplication(appId);
572 panel.indicators.hide()
582 shortcut: Qt.MetaModifier | Qt.Key_A
584 launcher.openDrawer(true);
588 shortcut: Qt.AltModifier | Qt.Key_F1
590 launcher.openForKeyboardNavigation();
594 shortcut: Qt.MetaModifier | Qt.Key_0
596 if (LauncherModel.get(9)) {
597 activateApplication(LauncherModel.get(9).appId);
604 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
606 if (LauncherModel.get(index)) {
607 activateApplication(LauncherModel.get(index).appId);
614 KeyboardShortcutsOverlay {
615 objectName: "shortcutsOverlay"
616 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
617 && height < parent.height - padding - panel.panelHeight
618 anchors.centerIn: parent
619 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
620 anchors.verticalCenterOffset: panel.panelHeight/2
622 opacity: enabled ? 0.95 : 0
624 Behavior on opacity {
625 UbuntuNumberAnimation {}
631 objectName: "tutorial"
634 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
635 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
636 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
637 inputMethod.visible ||
638 (launcher.shown && !launcher.lockedVisible) ||
639 panel.indicators.shown || stage.rightEdgeDragProgress > 0
640 usageScenario: shell.usageScenario
641 lastInputTimestamp: inputFilter.lastInputTimestamp
651 deferred: shell.mode === "greeter"
653 function unlockWhenDoneWithWizard() {
655 Connectivity.unlockAllModems();
659 Component.onCompleted: unlockWhenDoneWithWizard()
660 onActiveChanged: unlockWhenDoneWithWizard()
663 MouseArea { // modal notifications prevent interacting with other contents
665 visible: notifications.useModal
672 model: NotificationBackend.Model
674 hasMouse: shell.hasMouse
675 background: wallpaperResolver.cachedBackground
677 y: topmostIsFullscreen ? 0 : panel.panelHeight
678 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
683 when: overlay.width <= units.gu(60)
685 target: notifications
686 anchors.left: parent.left
687 anchors.right: parent.right
692 when: overlay.width > units.gu(60)
694 target: notifications
695 anchors.left: undefined
696 anchors.right: parent.right
698 PropertyChanges { target: notifications; width: units.gu(38) }
705 enabled: !greeter.shown
707 // NB: it does its own positioning according to the specified edge
711 panel.indicators.hide()
714 material: Component {
720 anchors.centerIn: parent
722 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
723 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
733 objectName: "dialogs"
735 visible: hasActiveDialog
737 usageScenario: shell.usageScenario
738 hasKeyboard: shell.hasKeyboard
740 shutdownFadeOutRectangle.enabled = true;
741 shutdownFadeOutRectangle.visible = true;
742 shutdownFadeOut.start();
747 target: SessionBroadcast
748 onShowHome: if (shell.mode !== "greeter") showHome()
753 objectName: "urlDispatcher"
754 active: shell.mode === "greeter"
755 onUrlRequested: shell.activateURL(url)
762 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
765 ignoreUnknownSignals: true
766 onItemSnapshotRequested: itemGrabber.capture(item)
771 id: cursorHidingTimer
773 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
774 onTriggered: cursor.opacity = 0;
780 visible: shell.hasMouse
782 topBoundaryOffset: panel.panelHeight
784 confiningItem: stage.itemConfiningMouseCursor
786 property bool mouseNeverMoved: true
788 target: cursor; property: "x"; value: shell.width / 2
789 when: cursor.mouseNeverMoved && cursor.visible
792 target: cursor; property: "y"; value: shell.height / 2
793 when: cursor.mouseNeverMoved && cursor.visible
798 readonly property var previewRectangle: stage.previewRectangle.target &&
799 stage.previewRectangle.target.dragging ?
800 stage.previewRectangle : null
802 onPushedLeftBoundary: {
803 if (buttons === Qt.NoButton) {
804 launcher.pushEdge(amount);
805 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
806 previewRectangle.maximizeLeft(amount);
810 onPushedRightBoundary: {
811 if (buttons === Qt.NoButton) {
812 rightEdgeBarrier.push(amount);
813 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
814 previewRectangle.maximizeRight(amount);
818 onPushedTopBoundary: {
819 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
820 previewRectangle.maximize(amount);
823 onPushedTopLeftCorner: {
824 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
825 previewRectangle.maximizeTopLeft(amount);
828 onPushedTopRightCorner: {
829 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
830 previewRectangle.maximizeTopRight(amount);
833 onPushedBottomLeftCorner: {
834 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
835 previewRectangle.maximizeBottomLeft(amount);
838 onPushedBottomRightCorner: {
839 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
840 previewRectangle.maximizeBottomRight(amount);
844 if (previewRectangle) {
845 previewRectangle.stop();
850 mouseNeverMoved = false;
854 Behavior on opacity { UbuntuNumberAnimation {} }
857 // non-visual objects
859 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
864 id: shutdownFadeOutRectangle
871 NumberAnimation on opacity {
876 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
877 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