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 0.1
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 indicatorAreaShowProgress: panel.indicatorAreaShowProgress
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
72 // to be read from outside
73 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
75 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
76 && stage.orientationChangesEnabled
77 && (!greeter || !greeter.animating)
79 readonly property bool showingGreeter: greeter && greeter.shown
81 property bool startingUp: true
82 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
84 property int supportedOrientations: {
86 // Ensure we don't rotate during start up
87 return Qt.PrimaryOrientation;
88 } else if (showingGreeter || notifications.topmostIsFullscreen) {
89 return Qt.PrimaryOrientation;
91 return shell.orientations.map(stage.supportedOrientations);
95 readonly property var mainApp: stage.mainApp
99 _onMainAppChanged(mainApp.appId);
103 target: ApplicationManager
105 if (shell.mainApp && shell.mainApp.appId === appId) {
106 _onMainAppChanged(appId);
110 function _onMainAppChanged(appId) {
111 if (wizard.active && appId != "" && appId != "unity8-dash") {
112 // If this happens on first boot, we may be in the
113 // wizard while receiving a call. But a call is more
114 // important than the wizard so just bail out of it.
118 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
119 // If we are in the middle of a call, make dialer lockedApp and show it.
120 // This can happen if user backs out of dialer back to greeter, then
121 // launches dialer again.
122 greeter.lockedApp = appId;
124 greeter.notifyAppFocusRequested(appId);
126 panel.indicators.hide();
130 // For autopilot consumption
131 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
133 // Note when greeter is waiting on PAM, so that we can disable edges until
134 // we know which user data to show and whether the session is locked.
135 readonly property bool waitingOnGreeter: greeter && greeter.waiting
137 property real edgeSize: units.gu(settings.edgeDragWidth)
140 id: wallpaperResolver
141 objectName: "wallpaperResolver"
143 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
144 readonly property bool hasCustomBackground: background != defaultBackground
146 // Use a cached version of the scaled-down wallpaper (as sometimes the
147 // image can be quite big compared to the device size, including for
148 // our default wallpaper). We use a name=wallpaper argument here to
149 // make sure we don't litter our cache with lots of scaled images. We
150 // only need to bother caching one at a time.
151 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
154 id: backgroundSettings
155 schema.id: "org.gnome.desktop.background"
159 AccountsService.backgroundFile,
160 backgroundSettings.pictureUri,
165 readonly property alias greeter: greeterLoader.item
167 function activateApplication(appId) {
168 // Either open the app in our own session, or -- if we're acting as a
169 // greeter -- ask the user's session to open it for us.
170 if (shell.mode === "greeter") {
171 activateURL("application:///" + appId + ".desktop");
177 function activateURL(url) {
178 SessionBroadcast.requestUrlStart(AccountsService.user, url);
179 greeter.notifyUserRequestedApp();
180 panel.indicators.hide();
183 function startApp(appId) {
184 if (ApplicationManager.findApplication(appId)) {
185 ApplicationManager.requestFocusApplication(appId);
187 ApplicationManager.startApplication(appId);
191 function startLockedApp(app) {
192 if (greeter.locked) {
193 greeter.lockedApp = app;
195 startApp(app); // locked apps are always in our same session
199 target: LauncherModel
200 property: "applicationManager"
201 value: ApplicationManager
204 Component.onCompleted: {
205 finishStartUpTimer.start();
214 objectName: "dashCommunicator"
218 id: physicalKeysMapper
219 objectName: "physicalKeysMapper"
221 onPowerKeyLongPressed: dialogs.showPowerDialog();
222 onVolumeDownTriggered: volumeControl.volumeDown();
223 onVolumeUpTriggered: volumeControl.volumeUp();
224 onScreenshotTriggered: itemGrabber.capture(shell);
228 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
233 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
234 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
238 objectName: "windowInputMonitor"
239 onHomeKeyActivated: {
240 // Ignore when greeter is active, to avoid pocket presses
241 if (!greeter.active) {
246 onTouchBegun: { cursor.opacity = 0; }
248 // move the (hidden) cursor to the last known touch position
249 var mappedCoords = mapFromItem(null, pos.x, pos.y);
250 cursor.x = mappedCoords.x;
251 cursor.y = mappedCoords.y;
252 cursor.mouseNeverMoved = false;
258 schema.id: "com.canonical.Unity8"
265 height: parent.height
267 TopLevelSurfaceList {
268 id: topLevelSurfaceList
269 objectName: "topLevelSurfaceList"
270 applicationsModel: ApplicationManager
279 dragAreaWidth: shell.edgeSize
280 background: wallpaperResolver.background
281 leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
282 Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
284 applicationManager: ApplicationManager
285 topLevelSurfaceList: topLevelSurfaceList
286 inputMethodRect: inputMethod.visibleRect
287 rightEdgePushProgress: rightEdgeBarrier.progress
289 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
291 : shell.usageScenario
293 mode: usageScenario == "phone" ? "staged"
294 : usageScenario == "tablet" ? "stagedWithSideStage"
297 shellOrientation: shell.orientation
298 shellOrientationAngle: shell.orientationAngle
299 orientations: shell.orientations
300 nativeWidth: shell.nativeWidth
301 nativeHeight: shell.nativeHeight
303 interactive: (!greeter || !greeter.shown)
304 && panel.indicators.fullyClosed
305 && launcher.progress == 0
306 && !notifications.useModal
308 onInteractiveChanged: { if (interactive) { focus = true; } }
310 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
311 suspended: greeter.shown
312 keepDashRunning: launcher.shown || launcher.dashSwipe
313 altTabPressed: physicalKeysMapper.altTabPressed
314 oskEnabled: shell.oskEnabled
315 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
321 objectName: "inputMethod"
324 topMargin: panel.panelHeight
325 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
327 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
332 objectName: "greeterLoader"
334 anchors.topMargin: panel.panelHeight
335 sourceComponent: shell.mode != "shell" ? integratedGreeter :
336 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
338 item.objectName = "greeter"
343 id: integratedGreeter
346 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
347 hides: [launcher, panel.indicators]
348 tabletMode: shell.usageScenario != "phone"
349 launcherOffset: launcher.progress
350 forcedUnlock: wizard.active || shell.mode === "full-shell"
351 background: wallpaperResolver.cachedBackground
352 hasCustomBackground: wallpaperResolver.hasCustomBackground
353 allowFingerprint: !dialogs.hasActiveDialog &&
354 !notifications.topmostIsFullscreen &&
355 !panel.indicators.shown
357 // avoid overlapping with Launcher's edge drag area
358 // FIXME: Fix TouchRegistry & friends and remove this workaround
359 // Issue involves launcher's DDA getting disabled on a long
361 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
368 if (!tutorial.running) {
373 onEmergencyCall: startLockedApp("dialer-app")
378 // See powerConnection for why this is useful
379 id: showGreeterDelayed
391 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
392 // We just received an incoming call while locked. The
393 // indicator will have already launched dialer-app for us, but
394 // there is a race between "hasCalls" changing and the dialer
395 // starting up. So in case we lose that race, we'll start/
396 // focus the dialer ourselves here too. Even if the indicator
397 // didn't launch the dialer for some reason (or maybe a call
398 // started via some other means), if an active call is
399 // happening, we want to be in the dialer.
400 startLockedApp("dialer-app")
410 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
411 !callManager.hasCalls && !wizard.active) {
412 // We don't want to simply call greeter.showNow() here, because
413 // that will take too long. Qt will delay button event
414 // handling until the greeter is done loading and may think the
415 // user held down the power button the whole time, leading to a
416 // power dialog being shown. Instead, delay showing the
417 // greeter until we've finished handling the event. We could
418 // make the greeter load asynchronously instead, but that
419 // introduces a whole host of timing issues, especially with
420 // its animations. So this is simpler.
421 showGreeterDelayed.start();
426 function showHome() {
427 greeter.notifyUserRequestedApp();
429 if (shell.mode === "greeter") {
430 SessionBroadcast.requestHomeShown(AccountsService.user);
432 var animate = !LightDMService.greeter.active && !stages.shown;
433 dash.setCurrentScope(0, animate, false);
434 ApplicationManager.requestFocusApplication("unity8-dash");
438 function showDash() {
439 if (greeter.notifyShowingDashFromDrag()) {
443 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
444 && (ApplicationManager.focusedApplicationId != "unity8-dash" || stage.spreadShown)) {
445 ApplicationManager.requestFocusApplication("unity8-dash")
460 anchors.fill: parent //because this draws indicator menus
463 available: tutorial.panelEnabled
464 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
465 && (!greeter || !greeter.hasLockedApp)
466 && !shell.waitingOnGreeter
467 && settings.enableIndicatorMenu
468 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
470 minimizedPanelHeight: units.gu(3)
471 expandedPanelHeight: units.gu(7)
473 indicatorsModel: Indicators.IndicatorsModel {
474 // tablet and phone both use the same profile
475 // FIXME: use just "phone" for greeter too, but first fix
476 // greeter app launching to either load the app inside the
477 // greeter or tell the session to load the app. This will
478 // involve taking the url-dispatcher dbus name and using
479 // SessionBroadcast to tell the session.
480 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
481 Component.onCompleted: load();
486 greeterShown: greeter.shown
489 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
490 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
492 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
493 || greeter.hasLockedApp
494 locked: greeter && greeter.active
499 objectName: "launcher"
502 * Since the Dash doesn't have the same controll over surfaces that the
503 * Shell does, it can't slowly move the scope out of the way, as the shell
504 * does with apps, and the dash is show instantly. This allows for some
505 * leeway and prevents accidental home swipes.
507 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
508 readonly property bool dashSwipe: progress > offset
510 anchors.top: parent.top
511 anchors.topMargin: inverted ? 0 : panel.panelHeight
512 anchors.bottom: parent.bottom
514 dragAreaWidth: shell.edgeSize
515 available: tutorial.launcherEnabled
516 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
517 && !greeter.hasLockedApp
518 && !shell.waitingOnGreeter
519 && settings.enableLauncher
520 inverted: shell.usageScenario !== "desktop"
521 superPressed: physicalKeysMapper.superPressed
522 superTabPressed: physicalKeysMapper.superTabPressed
523 panelWidth: units.gu(settings.launcherWidth)
524 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
526 onShowDashHome: showHome()
528 onDashSwipeChanged: {
530 dash.setCurrentScope(0, false, true)
533 onLauncherApplicationSelected: {
534 greeter.notifyUserRequestedApp();
535 shell.activateApplication(appId);
539 panel.indicators.hide()
549 shortcut: Qt.AltModifier | Qt.Key_F1
551 launcher.openForKeyboardNavigation();
555 shortcut: Qt.MetaModifier | Qt.Key_0
557 if (LauncherModel.get(9)) {
558 activateApplication(LauncherModel.get(9).appId);
565 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
567 if (LauncherModel.get(index)) {
568 activateApplication(LauncherModel.get(index).appId);
575 KeyboardShortcutsOverlay {
576 objectName: "shortcutsOverlay"
577 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
578 && height < parent.height - padding - panel.panelHeight
579 anchors.centerIn: parent
580 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
581 anchors.verticalCenterOffset: panel.panelHeight/2
583 opacity: enabled ? 0.95 : 0
585 Behavior on opacity {
586 UbuntuNumberAnimation {}
592 objectName: "tutorial"
595 paused: callManager.hasCalls || !greeter || greeter.active ||
597 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
598 inputMethod.visible ||
599 (launcher.shown && !launcher.lockedVisible) ||
600 panel.indicators.shown || stage.rightEdgeDragProgress > 0
601 usageScenario: shell.usageScenario
602 lastInputTimestamp: inputFilter.lastInputTimestamp
612 deferred: shell.mode === "greeter"
614 function unlockWhenDoneWithWizard() {
616 Connectivity.unlockAllModems();
620 Component.onCompleted: unlockWhenDoneWithWizard()
621 onActiveChanged: unlockWhenDoneWithWizard()
624 MouseArea { // modal notifications prevent interacting with other contents
626 visible: notifications.useModal
633 model: NotificationBackend.Model
635 hasMouse: shell.hasMouse
636 background: wallpaperResolver.cachedBackground
638 y: topmostIsFullscreen ? 0 : panel.panelHeight
639 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
644 when: overlay.width <= units.gu(60)
646 target: notifications
647 anchors.left: parent.left
648 anchors.right: parent.right
653 when: overlay.width > units.gu(60)
655 target: notifications
656 anchors.left: undefined
657 anchors.right: parent.right
659 PropertyChanges { target: notifications; width: units.gu(38) }
666 enabled: !greeter.shown
668 // NB: it does its own positioning according to the specified edge
672 panel.indicators.hide()
675 material: Component {
681 anchors.centerIn: parent
683 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
684 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
694 objectName: "dialogs"
697 usageScenario: shell.usageScenario
699 shutdownFadeOutRectangle.enabled = true;
700 shutdownFadeOutRectangle.visible = true;
701 shutdownFadeOut.start();
706 target: SessionBroadcast
707 onShowHome: if (shell.mode !== "greeter") showHome()
712 objectName: "urlDispatcher"
713 active: shell.mode === "greeter"
714 onUrlRequested: shell.activateURL(url)
721 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
724 ignoreUnknownSignals: true
725 onItemSnapshotRequested: itemGrabber.capture(item)
730 id: cursorHidingTimer
732 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
733 onTriggered: cursor.opacity = 0;
739 visible: shell.hasMouse
741 topBoundaryOffset: panel.panelHeight
743 confiningItem: stage.itemConfiningMouseCursor
745 property bool mouseNeverMoved: true
747 target: cursor; property: "x"; value: shell.width / 2
748 when: cursor.mouseNeverMoved && cursor.visible
751 target: cursor; property: "y"; value: shell.height / 2
752 when: cursor.mouseNeverMoved && cursor.visible
757 readonly property var previewRectangle: stage.previewRectangle.target &&
758 stage.previewRectangle.target.dragging ?
759 stage.previewRectangle : null
761 onPushedLeftBoundary: {
762 if (buttons === Qt.NoButton) {
763 launcher.pushEdge(amount);
764 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
765 previewRectangle.maximizeLeft(amount);
769 onPushedRightBoundary: {
770 if (buttons === Qt.NoButton) {
771 rightEdgeBarrier.push(amount);
772 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
773 previewRectangle.maximizeRight(amount);
777 onPushedTopBoundary: {
778 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
779 previewRectangle.maximize(amount);
782 onPushedTopLeftCorner: {
783 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
784 previewRectangle.maximizeTopLeft(amount);
787 onPushedTopRightCorner: {
788 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
789 previewRectangle.maximizeTopRight(amount);
792 onPushedBottomLeftCorner: {
793 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
794 previewRectangle.maximizeBottomLeft(amount);
797 onPushedBottomRightCorner: {
798 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
799 previewRectangle.maximizeBottomRight(amount);
803 if (previewRectangle) {
804 previewRectangle.stop();
809 mouseNeverMoved = false;
813 Behavior on opacity { UbuntuNumberAnimation {} }
816 // non-visual objects
821 id: shutdownFadeOutRectangle
828 NumberAnimation on opacity {
833 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
834 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