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
438 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
439 // We just received an incoming call while locked. The
440 // indicator will have already launched dialer-app for us, but
441 // there is a race between "hasCalls" changing and the dialer
442 // starting up. So in case we lose that race, we'll start/
443 // focus the dialer ourselves here too. Even if the indicator
444 // didn't launch the dialer for some reason (or maybe a call
445 // started via some other means), if an active call is
446 // happening, we want to be in the dialer.
447 startLockedApp("dialer-app")
457 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
458 !callManager.hasCalls && !wizard.active) {
459 // We don't want to simply call greeter.showNow() here, because
460 // that will take too long. Qt will delay button event
461 // handling until the greeter is done loading and may think the
462 // user held down the power button the whole time, leading to a
463 // power dialog being shown. Instead, delay showing the
464 // greeter until we've finished handling the event. We could
465 // make the greeter load asynchronously instead, but that
466 // introduces a whole host of timing issues, especially with
467 // its animations. So this is simpler.
468 showGreeterDelayed.start();
473 function showHome() {
474 greeter.notifyUserRequestedApp();
476 if (shell.mode === "greeter") {
477 SessionBroadcast.requestHomeShown(AccountsService.user);
479 if (!greeter.active) {
480 launcher.openDrawer(false);
482 greeterLoader.openDrawerAfterUnlock = true;
496 anchors.fill: parent //because this draws indicator menus
498 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
499 minimizedPanelHeight: units.gu(3)
500 expandedPanelHeight: units.gu(7)
501 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
502 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
506 available: tutorial.panelEnabled
507 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
508 && (!greeter || !greeter.hasLockedApp)
509 && !shell.waitingOnGreeter
510 && settings.enableIndicatorMenu
512 model: Indicators.IndicatorsModel {
513 // tablet and phone both use the same profile
514 // FIXME: use just "phone" for greeter too, but first fix
515 // greeter app launching to either load the app inside the
516 // greeter or tell the session to load the app. This will
517 // involve taking the url-dispatcher dbus name and using
518 // SessionBroadcast to tell the session.
519 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
520 Component.onCompleted: load();
526 available: (!greeter || !greeter.shown)
527 && !shell.waitingOnGreeter
528 && !stage.spreadShown
531 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
532 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
534 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
535 || greeter.hasLockedApp
536 greeterShown: greeter && greeter.shown
537 hasKeyboard: shell.hasKeyboard
542 objectName: "launcher"
544 anchors.top: parent.top
545 anchors.topMargin: inverted ? 0 : panel.panelHeight
546 anchors.bottom: parent.bottom
548 dragAreaWidth: shell.edgeSize
549 available: tutorial.launcherEnabled
550 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
551 && !greeter.hasLockedApp
552 && !shell.waitingOnGreeter
553 && settings.enableLauncher
554 inverted: shell.usageScenario !== "desktop"
555 superPressed: physicalKeysMapper.superPressed
556 superTabPressed: physicalKeysMapper.superTabPressed
557 panelWidth: units.gu(settings.launcherWidth)
558 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
559 blurSource: greeter.shown ? greeter : stages
560 topPanelHeight: panel.panelHeight
561 drawerEnabled: !greeter.active
563 onShowDashHome: showHome()
564 onLauncherApplicationSelected: {
565 greeter.notifyUserRequestedApp();
566 shell.activateApplication(appId);
570 panel.indicators.hide()
580 shortcut: Qt.MetaModifier | Qt.Key_A
582 launcher.openDrawer(true);
586 shortcut: Qt.AltModifier | Qt.Key_F1
588 launcher.openForKeyboardNavigation();
592 shortcut: Qt.MetaModifier | Qt.Key_0
594 if (LauncherModel.get(9)) {
595 activateApplication(LauncherModel.get(9).appId);
602 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
604 if (LauncherModel.get(index)) {
605 activateApplication(LauncherModel.get(index).appId);
612 KeyboardShortcutsOverlay {
613 objectName: "shortcutsOverlay"
614 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
615 && height < parent.height - padding - panel.panelHeight
616 anchors.centerIn: parent
617 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
618 anchors.verticalCenterOffset: panel.panelHeight/2
620 opacity: enabled ? 0.95 : 0
622 Behavior on opacity {
623 UbuntuNumberAnimation {}
629 objectName: "tutorial"
632 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
633 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
634 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
635 inputMethod.visible ||
636 (launcher.shown && !launcher.lockedVisible) ||
637 panel.indicators.shown || stage.rightEdgeDragProgress > 0
638 usageScenario: shell.usageScenario
639 lastInputTimestamp: inputFilter.lastInputTimestamp
649 deferred: shell.mode === "greeter"
651 function unlockWhenDoneWithWizard() {
653 Connectivity.unlockAllModems();
657 Component.onCompleted: unlockWhenDoneWithWizard()
658 onActiveChanged: unlockWhenDoneWithWizard()
661 MouseArea { // modal notifications prevent interacting with other contents
663 visible: notifications.useModal
670 model: NotificationBackend.Model
672 hasMouse: shell.hasMouse
673 background: wallpaperResolver.cachedBackground
675 y: topmostIsFullscreen ? 0 : panel.panelHeight
676 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
681 when: overlay.width <= units.gu(60)
683 target: notifications
684 anchors.left: parent.left
685 anchors.right: parent.right
690 when: overlay.width > units.gu(60)
692 target: notifications
693 anchors.left: undefined
694 anchors.right: parent.right
696 PropertyChanges { target: notifications; width: units.gu(38) }
703 enabled: !greeter.shown
705 // NB: it does its own positioning according to the specified edge
709 panel.indicators.hide()
712 material: Component {
718 anchors.centerIn: parent
720 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
721 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
731 objectName: "dialogs"
733 visible: hasActiveDialog
735 usageScenario: shell.usageScenario
736 hasKeyboard: shell.hasKeyboard
738 shutdownFadeOutRectangle.enabled = true;
739 shutdownFadeOutRectangle.visible = true;
740 shutdownFadeOut.start();
745 target: SessionBroadcast
746 onShowHome: if (shell.mode !== "greeter") showHome()
751 objectName: "urlDispatcher"
752 active: shell.mode === "greeter"
753 onUrlRequested: shell.activateURL(url)
760 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
763 ignoreUnknownSignals: true
764 onItemSnapshotRequested: itemGrabber.capture(item)
769 id: cursorHidingTimer
771 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
772 onTriggered: cursor.opacity = 0;
778 visible: shell.hasMouse
780 topBoundaryOffset: panel.panelHeight
782 confiningItem: stage.itemConfiningMouseCursor
784 property bool mouseNeverMoved: true
786 target: cursor; property: "x"; value: shell.width / 2
787 when: cursor.mouseNeverMoved && cursor.visible
790 target: cursor; property: "y"; value: shell.height / 2
791 when: cursor.mouseNeverMoved && cursor.visible
796 readonly property var previewRectangle: stage.previewRectangle.target &&
797 stage.previewRectangle.target.dragging ?
798 stage.previewRectangle : null
800 onPushedLeftBoundary: {
801 if (buttons === Qt.NoButton) {
802 launcher.pushEdge(amount);
803 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
804 previewRectangle.maximizeLeft(amount);
808 onPushedRightBoundary: {
809 if (buttons === Qt.NoButton) {
810 rightEdgeBarrier.push(amount);
811 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
812 previewRectangle.maximizeRight(amount);
816 onPushedTopBoundary: {
817 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
818 previewRectangle.maximize(amount);
821 onPushedTopLeftCorner: {
822 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
823 previewRectangle.maximizeTopLeft(amount);
826 onPushedTopRightCorner: {
827 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
828 previewRectangle.maximizeTopRight(amount);
831 onPushedBottomLeftCorner: {
832 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
833 previewRectangle.maximizeBottomLeft(amount);
836 onPushedBottomRightCorner: {
837 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
838 previewRectangle.maximizeBottomRight(amount);
842 if (previewRectangle) {
843 previewRectangle.stop();
848 mouseNeverMoved = false;
852 Behavior on opacity { UbuntuNumberAnimation {} }
855 // non-visual objects
857 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
862 id: shutdownFadeOutRectangle
869 NumberAnimation on opacity {
874 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
875 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