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.toggleDrawer(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.toggleDrawer(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 toggleDrawerAfterUnlock: false
385 // Show drawer in case showHome() requests it
386 if (greeterLoader.toggleDrawerAfterUnlock) {
387 launcher.toggleDrawer(false);
388 greeterLoader.toggleDrawerAfterUnlock = false;
390 // Show the launcher when there are no running apps
391 else if (topLevelSurfaceList.count < 1) {
392 launcher.switchToNextState("visible")
397 // Show the launcher in case there are no running apps
399 target: topLevelSurfaceList
401 if (topLevelSurfaceList.count > 0)
405 launcher.switchToNextState("visible")
411 id: integratedGreeter
414 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
415 hides: [launcher, panel.indicators, panel.applicationMenus]
416 tabletMode: shell.usageScenario != "phone"
417 forcedUnlock: wizard.active || shell.mode === "full-shell"
418 background: wallpaperResolver.cachedBackground
419 hasCustomBackground: wallpaperResolver.hasCustomBackground
420 allowFingerprint: !dialogs.hasActiveDialog &&
421 !notifications.topmostIsFullscreen &&
422 !panel.indicators.shown
424 // avoid overlapping with Launcher's edge drag area
425 // FIXME: Fix TouchRegistry & friends and remove this workaround
426 // Issue involves launcher's DDA getting disabled on a long
428 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
435 if (!tutorial.running) {
440 onEmergencyCall: startLockedApp("dialer-app")
445 // See powerConnection for why this is useful
446 id: showGreeterDelayed
449 // Go through the dbus service, because it has checks for whether
450 // we are even allowed to lock or not.
451 DBusUnitySessionService.PromptLock();
460 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
461 // We just received an incoming call while locked. The
462 // indicator will have already launched dialer-app for us, but
463 // there is a race between "hasCalls" changing and the dialer
464 // starting up. So in case we lose that race, we'll start/
465 // focus the dialer ourselves here too. Even if the indicator
466 // didn't launch the dialer for some reason (or maybe a call
467 // started via some other means), if an active call is
468 // happening, we want to be in the dialer.
469 startLockedApp("dialer-app")
479 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
480 !callManager.hasCalls && !wizard.active) {
481 // We don't want to simply call greeter.showNow() here, because
482 // that will take too long. Qt will delay button event
483 // handling until the greeter is done loading and may think the
484 // user held down the power button the whole time, leading to a
485 // power dialog being shown. Instead, delay showing the
486 // greeter until we've finished handling the event. We could
487 // make the greeter load asynchronously instead, but that
488 // introduces a whole host of timing issues, especially with
489 // its animations. So this is simpler.
490 showGreeterDelayed.start();
495 function showHome() {
496 greeter.notifyUserRequestedApp();
498 if (shell.mode === "greeter") {
499 SessionBroadcast.requestHomeShown(AccountsService.user);
501 if (!greeter.active) {
502 launcher.toggleDrawer(false);
504 greeterLoader.toggleDrawerAfterUnlock = true;
518 anchors.fill: parent //because this draws indicator menus
520 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
521 minimizedPanelHeight: units.gu(3)
522 expandedPanelHeight: units.gu(7)
523 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
524 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
525 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
529 available: tutorial.panelEnabled
530 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
531 && (!greeter || !greeter.hasLockedApp)
532 && !shell.waitingOnGreeter
533 && settings.enableIndicatorMenu
535 model: Indicators.IndicatorsModel {
536 // tablet and phone both use the same profile
537 // FIXME: use just "phone" for greeter too, but first fix
538 // greeter app launching to either load the app inside the
539 // greeter or tell the session to load the app. This will
540 // involve taking the url-dispatcher dbus name and using
541 // SessionBroadcast to tell the session.
542 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
543 Component.onCompleted: load();
549 available: (!greeter || !greeter.shown)
550 && !shell.waitingOnGreeter
551 && !stage.spreadShown
554 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
555 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
557 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
558 || greeter.hasLockedApp
559 greeterShown: greeter && greeter.shown
560 hasKeyboard: shell.hasKeyboard
565 objectName: "launcher"
567 anchors.top: parent.top
568 anchors.topMargin: inverted ? 0 : panel.panelHeight
569 anchors.bottom: parent.bottom
571 dragAreaWidth: shell.edgeSize
572 available: tutorial.launcherEnabled
573 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
574 && !greeter.hasLockedApp
575 && !shell.waitingOnGreeter
576 && settings.enableLauncher
577 inverted: shell.usageScenario !== "desktop"
578 superPressed: physicalKeysMapper.superPressed
579 superTabPressed: physicalKeysMapper.superTabPressed
580 panelWidth: units.gu(settings.launcherWidth)
581 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
582 topPanelHeight: panel.panelHeight
583 drawerEnabled: !greeter.active
584 privateMode: greeter.active
586 onShowDashHome: showHome()
587 onLauncherApplicationSelected: {
588 greeter.notifyUserRequestedApp();
589 shell.activateApplication(appId);
593 panel.indicators.hide();
594 panel.applicationMenus.hide();
597 onDrawerShownChanged: {
599 panel.indicators.hide();
600 panel.applicationMenus.hide();
610 shortcut: Qt.MetaModifier | Qt.Key_A
612 launcher.toggleDrawer(true);
616 shortcut: Qt.AltModifier | Qt.Key_F1
618 launcher.openForKeyboardNavigation();
622 shortcut: Qt.MetaModifier | Qt.Key_0
624 if (LauncherModel.get(9)) {
625 activateApplication(LauncherModel.get(9).appId);
632 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
634 if (LauncherModel.get(index)) {
635 activateApplication(LauncherModel.get(index).appId);
642 KeyboardShortcutsOverlay {
643 objectName: "shortcutsOverlay"
644 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
645 && height < parent.height - padding - panel.panelHeight
646 anchors.centerIn: parent
647 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
648 anchors.verticalCenterOffset: panel.panelHeight/2
650 opacity: enabled ? 0.95 : 0
652 Behavior on opacity {
653 UbuntuNumberAnimation {}
659 objectName: "tutorial"
662 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
663 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
664 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
665 inputMethod.visible ||
666 (launcher.shown && !launcher.lockedVisible) ||
667 panel.indicators.shown || stage.rightEdgeDragProgress > 0
668 usageScenario: shell.usageScenario
669 lastInputTimestamp: inputFilter.lastInputTimestamp
679 deferred: shell.mode === "greeter"
681 function unlockWhenDoneWithWizard() {
683 Connectivity.unlockAllModems();
687 Component.onCompleted: unlockWhenDoneWithWizard()
688 onActiveChanged: unlockWhenDoneWithWizard()
691 MouseArea { // modal notifications prevent interacting with other contents
693 visible: notifications.useModal
700 model: NotificationBackend.Model
702 hasMouse: shell.hasMouse
703 background: wallpaperResolver.cachedBackground
705 y: topmostIsFullscreen ? 0 : panel.panelHeight
706 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
711 when: overlay.width <= units.gu(60)
713 target: notifications
714 anchors.left: parent.left
715 anchors.right: parent.right
720 when: overlay.width > units.gu(60)
722 target: notifications
723 anchors.left: undefined
724 anchors.right: parent.right
726 PropertyChanges { target: notifications; width: units.gu(38) }
733 enabled: !greeter.shown
735 // NB: it does its own positioning according to the specified edge
739 panel.indicators.hide()
742 material: Component {
748 anchors.centerIn: parent
750 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
751 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
761 objectName: "dialogs"
763 visible: hasActiveDialog
765 usageScenario: shell.usageScenario
766 hasKeyboard: shell.hasKeyboard
768 shutdownFadeOutRectangle.enabled = true;
769 shutdownFadeOutRectangle.visible = true;
770 shutdownFadeOut.start();
775 target: SessionBroadcast
776 onShowHome: if (shell.mode !== "greeter") showHome()
781 objectName: "urlDispatcher"
782 active: shell.mode === "greeter"
783 onUrlRequested: shell.activateURL(url)
790 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
793 ignoreUnknownSignals: true
794 onItemSnapshotRequested: itemGrabber.capture(item)
799 id: cursorHidingTimer
801 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
802 onTriggered: cursor.opacity = 0;
808 visible: shell.hasMouse
810 topBoundaryOffset: panel.panelHeight
812 confiningItem: stage.itemConfiningMouseCursor
814 property bool mouseNeverMoved: true
816 target: cursor; property: "x"; value: shell.width / 2
817 when: cursor.mouseNeverMoved && cursor.visible
820 target: cursor; property: "y"; value: shell.height / 2
821 when: cursor.mouseNeverMoved && cursor.visible
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: topLevelSurfaceList.focusedWindow ? 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