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.Indicators 0.1 as Indicators
45import WindowManager 1.0
51 theme.name: "Ubuntu.Components.Themes.SuruDark"
53 // to be set from outside
54 property int orientationAngle: 0
55 property int orientation
56 property Orientations orientations
57 property real nativeWidth
58 property real nativeHeight
59 property alias panelAreaShowProgress: panel.panelAreaShowProgress
60 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
61 property string mode: "full-greeter"
62 property alias oskEnabled: inputMethod.enabled
63 function updateFocusedAppOrientation() {
64 stage.updateFocusedAppOrientation();
66 function updateFocusedAppOrientationAnimated() {
67 stage.updateFocusedAppOrientationAnimated();
69 property bool hasMouse: false
70 property bool hasKeyboard: false
71 property bool hasTouchscreen: 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 != "") {
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();
214 id: physicalKeysMapper
215 objectName: "physicalKeysMapper"
217 onPowerKeyLongPressed: dialogs.showPowerDialog();
218 onVolumeDownTriggered: volumeControl.volumeDown();
219 onVolumeUpTriggered: volumeControl.volumeUp();
220 onScreenshotTriggered: itemGrabber.capture(shell);
224 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
229 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
230 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
234 objectName: "windowInputMonitor"
235 onHomeKeyActivated: {
236 // Ignore when greeter is active, to avoid pocket presses
237 if (!greeter.active) {
238 launcher.openDrawer(false);
241 onTouchBegun: { cursor.opacity = 0; }
243 // move the (hidden) cursor to the last known touch position
244 var mappedCoords = mapFromItem(null, pos.x, pos.y);
245 cursor.x = mappedCoords.x;
246 cursor.y = mappedCoords.y;
247 cursor.mouseNeverMoved = false;
251 AvailableDesktopArea {
252 id: availableDesktopAreaItem
254 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
255 anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
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
293 availableDesktopArea: availableDesktopAreaItem
295 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
297 : shell.usageScenario
299 mode: usageScenario == "phone" ? "staged"
300 : usageScenario == "tablet" ? "stagedWithSideStage"
303 shellOrientation: shell.orientation
304 shellOrientationAngle: shell.orientationAngle
305 orientations: shell.orientations
306 nativeWidth: shell.nativeWidth
307 nativeHeight: shell.nativeHeight
309 interactive: (!greeter || !greeter.shown)
310 && panel.indicators.fullyClosed
311 && !notifications.useModal
313 onInteractiveChanged: { if (interactive) { focus = true; } }
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
505 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
509 available: tutorial.panelEnabled
510 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
511 && (!greeter || !greeter.hasLockedApp)
512 && !shell.waitingOnGreeter
513 && settings.enableIndicatorMenu
515 model: Indicators.IndicatorsModel {
516 // tablet and phone both use the same profile
517 // FIXME: use just "phone" for greeter too, but first fix
518 // greeter app launching to either load the app inside the
519 // greeter or tell the session to load the app. This will
520 // involve taking the url-dispatcher dbus name and using
521 // SessionBroadcast to tell the session.
522 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
523 Component.onCompleted: load();
529 available: (!greeter || !greeter.shown)
530 && !shell.waitingOnGreeter
531 && !stage.spreadShown
534 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
535 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
537 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
538 || greeter.hasLockedApp
539 greeterShown: greeter && greeter.shown
540 hasKeyboard: shell.hasKeyboard
545 objectName: "launcher"
547 anchors.top: parent.top
548 anchors.topMargin: inverted ? 0 : panel.panelHeight
549 anchors.bottom: parent.bottom
551 dragAreaWidth: shell.edgeSize
552 available: tutorial.launcherEnabled
553 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
554 && !greeter.hasLockedApp
555 && !shell.waitingOnGreeter
556 && settings.enableLauncher
557 inverted: shell.usageScenario !== "desktop"
558 superPressed: physicalKeysMapper.superPressed
559 superTabPressed: physicalKeysMapper.superTabPressed
560 panelWidth: units.gu(settings.launcherWidth)
561 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
562 blurSource: greeter.shown ? greeter : stages
563 topPanelHeight: panel.panelHeight
564 drawerEnabled: !greeter.active
565 privateMode: greeter.active
567 onShowDashHome: showHome()
568 onLauncherApplicationSelected: {
569 greeter.notifyUserRequestedApp();
570 shell.activateApplication(appId);
574 panel.indicators.hide();
575 panel.applicationMenus.hide();
578 onDrawerShownChanged: {
580 panel.indicators.hide();
581 panel.applicationMenus.hide();
591 shortcut: Qt.MetaModifier | Qt.Key_A
593 launcher.openDrawer(true);
597 shortcut: Qt.AltModifier | Qt.Key_F1
599 launcher.openForKeyboardNavigation();
603 shortcut: Qt.MetaModifier | Qt.Key_0
605 if (LauncherModel.get(9)) {
606 activateApplication(LauncherModel.get(9).appId);
613 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
615 if (LauncherModel.get(index)) {
616 activateApplication(LauncherModel.get(index).appId);
623 KeyboardShortcutsOverlay {
624 objectName: "shortcutsOverlay"
625 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
626 && height < parent.height - padding - panel.panelHeight
627 anchors.centerIn: parent
628 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
629 anchors.verticalCenterOffset: panel.panelHeight/2
631 opacity: enabled ? 0.95 : 0
633 Behavior on opacity {
634 UbuntuNumberAnimation {}
640 objectName: "tutorial"
643 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
644 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
645 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
646 inputMethod.visible ||
647 (launcher.shown && !launcher.lockedVisible) ||
648 panel.indicators.shown || stage.rightEdgeDragProgress > 0
649 usageScenario: shell.usageScenario
650 lastInputTimestamp: inputFilter.lastInputTimestamp
660 deferred: shell.mode === "greeter"
662 function unlockWhenDoneWithWizard() {
664 Connectivity.unlockAllModems();
668 Component.onCompleted: unlockWhenDoneWithWizard()
669 onActiveChanged: unlockWhenDoneWithWizard()
672 MouseArea { // modal notifications prevent interacting with other contents
674 visible: notifications.useModal
681 model: NotificationBackend.Model
683 hasMouse: shell.hasMouse
684 background: wallpaperResolver.cachedBackground
686 y: topmostIsFullscreen ? 0 : panel.panelHeight
687 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
692 when: overlay.width <= units.gu(60)
694 target: notifications
695 anchors.left: parent.left
696 anchors.right: parent.right
701 when: overlay.width > units.gu(60)
703 target: notifications
704 anchors.left: undefined
705 anchors.right: parent.right
707 PropertyChanges { target: notifications; width: units.gu(38) }
714 enabled: !greeter.shown
716 // NB: it does its own positioning according to the specified edge
720 panel.indicators.hide()
723 material: Component {
729 anchors.centerIn: parent
731 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
732 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
742 objectName: "dialogs"
744 visible: hasActiveDialog
746 usageScenario: shell.usageScenario
747 hasKeyboard: shell.hasKeyboard
749 shutdownFadeOutRectangle.enabled = true;
750 shutdownFadeOutRectangle.visible = true;
751 shutdownFadeOut.start();
756 target: SessionBroadcast
757 onShowHome: if (shell.mode !== "greeter") showHome()
762 objectName: "urlDispatcher"
763 active: shell.mode === "greeter"
764 onUrlRequested: shell.activateURL(url)
771 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
774 ignoreUnknownSignals: true
775 onItemSnapshotRequested: itemGrabber.capture(item)
780 id: cursorHidingTimer
782 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
783 onTriggered: cursor.opacity = 0;
789 visible: shell.hasMouse
791 topBoundaryOffset: panel.panelHeight
793 confiningItem: stage.itemConfiningMouseCursor
795 property bool mouseNeverMoved: true
797 target: cursor; property: "x"; value: shell.width / 2
798 when: cursor.mouseNeverMoved && cursor.visible
801 target: cursor; property: "y"; value: shell.height / 2
802 when: cursor.mouseNeverMoved && cursor.visible
807 readonly property var previewRectangle: stage.previewRectangle.target &&
808 stage.previewRectangle.target.dragging ?
809 stage.previewRectangle : null
811 onPushedLeftBoundary: {
812 if (buttons === Qt.NoButton) {
813 launcher.pushEdge(amount);
814 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
815 previewRectangle.maximizeLeft(amount);
819 onPushedRightBoundary: {
820 if (buttons === Qt.NoButton) {
821 rightEdgeBarrier.push(amount);
822 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
823 previewRectangle.maximizeRight(amount);
827 onPushedTopBoundary: {
828 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
829 previewRectangle.maximize(amount);
832 onPushedTopLeftCorner: {
833 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
834 previewRectangle.maximizeTopLeft(amount);
837 onPushedTopRightCorner: {
838 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
839 previewRectangle.maximizeTopRight(amount);
842 onPushedBottomLeftCorner: {
843 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
844 previewRectangle.maximizeBottomLeft(amount);
847 onPushedBottomRightCorner: {
848 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
849 previewRectangle.maximizeBottomRight(amount);
853 if (previewRectangle) {
854 previewRectangle.stop();
859 mouseNeverMoved = false;
863 Behavior on opacity { UbuntuNumberAnimation {} }
866 // non-visual objects
868 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
873 id: shutdownFadeOutRectangle
880 NumberAnimation on opacity {
885 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
886 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