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
288 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
290 : shell.usageScenario
292 mode: usageScenario == "phone" ? "staged"
293 : usageScenario == "tablet" ? "stagedWithSideStage"
296 shellOrientation: shell.orientation
297 shellOrientationAngle: shell.orientationAngle
298 orientations: shell.orientations
299 nativeWidth: shell.nativeWidth
300 nativeHeight: shell.nativeHeight
302 interactive: (!greeter || !greeter.shown)
303 && panel.indicators.fullyClosed
304 && launcher.progress == 0
305 && !notifications.useModal
307 onInteractiveChanged: { if (interactive) { focus = true; } }
309 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
310 suspended: greeter.shown
311 keepDashRunning: launcher.shown || launcher.dashSwipe
312 altTabPressed: physicalKeysMapper.altTabPressed
313 oskEnabled: shell.oskEnabled
314 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
320 objectName: "inputMethod"
323 topMargin: panel.panelHeight
324 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
326 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
331 objectName: "greeterLoader"
333 anchors.topMargin: panel.panelHeight
334 sourceComponent: shell.mode != "shell" ? integratedGreeter :
335 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
337 item.objectName = "greeter"
342 id: integratedGreeter
345 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
346 hides: [launcher, panel.indicators]
347 tabletMode: shell.usageScenario != "phone"
348 launcherOffset: launcher.progress
349 forcedUnlock: wizard.active || shell.mode === "full-shell"
350 background: wallpaperResolver.cachedBackground
351 hasCustomBackground: wallpaperResolver.hasCustomBackground
352 allowFingerprint: !dialogs.hasActiveDialog &&
353 !notifications.topmostIsFullscreen &&
354 !panel.indicators.shown
356 // avoid overlapping with Launcher's edge drag area
357 // FIXME: Fix TouchRegistry & friends and remove this workaround
358 // Issue involves launcher's DDA getting disabled on a long
360 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
367 if (!tutorial.running) {
372 onEmergencyCall: startLockedApp("dialer-app")
377 // See powerConnection for why this is useful
378 id: showGreeterDelayed
390 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
391 // We just received an incoming call while locked. The
392 // indicator will have already launched dialer-app for us, but
393 // there is a race between "hasCalls" changing and the dialer
394 // starting up. So in case we lose that race, we'll start/
395 // focus the dialer ourselves here too. Even if the indicator
396 // didn't launch the dialer for some reason (or maybe a call
397 // started via some other means), if an active call is
398 // happening, we want to be in the dialer.
399 startLockedApp("dialer-app")
409 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
410 !callManager.hasCalls && !wizard.active) {
411 // We don't want to simply call greeter.showNow() here, because
412 // that will take too long. Qt will delay button event
413 // handling until the greeter is done loading and may think the
414 // user held down the power button the whole time, leading to a
415 // power dialog being shown. Instead, delay showing the
416 // greeter until we've finished handling the event. We could
417 // make the greeter load asynchronously instead, but that
418 // introduces a whole host of timing issues, especially with
419 // its animations. So this is simpler.
420 showGreeterDelayed.start();
425 function showHome() {
426 greeter.notifyUserRequestedApp();
428 if (shell.mode === "greeter") {
429 SessionBroadcast.requestHomeShown(AccountsService.user);
431 var animate = !LightDMService.greeter.active && !stages.shown;
432 dash.setCurrentScope(0, animate, false);
433 ApplicationManager.requestFocusApplication("unity8-dash");
437 function showDash() {
438 if (greeter.notifyShowingDashFromDrag()) {
442 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
443 && (ApplicationManager.focusedApplicationId != "unity8-dash" || stage.spreadShown)) {
444 ApplicationManager.requestFocusApplication("unity8-dash")
459 anchors.fill: parent //because this draws indicator menus
462 available: tutorial.panelEnabled
463 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
464 && (!greeter || !greeter.hasLockedApp)
465 && !shell.waitingOnGreeter
466 && settings.enableIndicatorMenu
467 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
469 minimizedPanelHeight: units.gu(3)
470 expandedPanelHeight: units.gu(7)
472 indicatorsModel: Indicators.IndicatorsModel {
473 // tablet and phone both use the same profile
474 // FIXME: use just "phone" for greeter too, but first fix
475 // greeter app launching to either load the app inside the
476 // greeter or tell the session to load the app. This will
477 // involve taking the url-dispatcher dbus name and using
478 // SessionBroadcast to tell the session.
479 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
480 Component.onCompleted: load();
485 greeterShown: greeter.shown
488 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
489 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
491 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
492 || greeter.hasLockedApp
493 locked: greeter && greeter.active
498 objectName: "launcher"
501 * Since the Dash doesn't have the same controll over surfaces that the
502 * Shell does, it can't slowly move the scope out of the way, as the shell
503 * does with apps, and the dash is show instantly. This allows for some
504 * leeway and prevents accidental home swipes.
506 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
507 readonly property bool dashSwipe: progress > offset
509 anchors.top: parent.top
510 anchors.topMargin: inverted ? 0 : panel.panelHeight
511 anchors.bottom: parent.bottom
513 dragAreaWidth: shell.edgeSize
514 available: tutorial.launcherEnabled
515 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
516 && !greeter.hasLockedApp
517 && !shell.waitingOnGreeter
518 && settings.enableLauncher
519 inverted: shell.usageScenario !== "desktop"
520 superPressed: physicalKeysMapper.superPressed
521 superTabPressed: physicalKeysMapper.superTabPressed
522 panelWidth: units.gu(settings.launcherWidth)
523 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
525 onShowDashHome: showHome()
527 onDashSwipeChanged: {
529 dash.setCurrentScope(0, false, true)
532 onLauncherApplicationSelected: {
533 greeter.notifyUserRequestedApp();
534 shell.activateApplication(appId);
538 panel.indicators.hide()
548 shortcut: Qt.AltModifier | Qt.Key_F1
550 launcher.openForKeyboardNavigation();
554 shortcut: Qt.MetaModifier | Qt.Key_0
556 if (LauncherModel.get(9)) {
557 activateApplication(LauncherModel.get(9).appId);
564 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
566 if (LauncherModel.get(index)) {
567 activateApplication(LauncherModel.get(index).appId);
574 KeyboardShortcutsOverlay {
575 objectName: "shortcutsOverlay"
576 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
577 && height < parent.height - padding - panel.panelHeight
578 anchors.centerIn: parent
579 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
580 anchors.verticalCenterOffset: panel.panelHeight/2
582 opacity: enabled ? 0.95 : 0
584 Behavior on opacity {
585 UbuntuNumberAnimation {}
591 objectName: "tutorial"
594 paused: callManager.hasCalls || !greeter || greeter.active ||
596 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
597 inputMethod.visible ||
598 (launcher.shown && !launcher.lockedVisible) ||
599 panel.indicators.shown || stage.rightEdgeDragProgress > 0
600 usageScenario: shell.usageScenario
601 lastInputTimestamp: inputFilter.lastInputTimestamp
611 deferred: shell.mode === "greeter"
613 function unlockWhenDoneWithWizard() {
615 Connectivity.unlockAllModems();
619 Component.onCompleted: unlockWhenDoneWithWizard()
620 onActiveChanged: unlockWhenDoneWithWizard()
623 MouseArea { // modal notifications prevent interacting with other contents
625 visible: notifications.useModal
632 model: NotificationBackend.Model
634 hasMouse: shell.hasMouse
635 background: wallpaperResolver.cachedBackground
637 y: topmostIsFullscreen ? 0 : panel.panelHeight
638 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
643 when: overlay.width <= units.gu(60)
645 target: notifications
646 anchors.left: parent.left
647 anchors.right: parent.right
652 when: overlay.width > units.gu(60)
654 target: notifications
655 anchors.left: undefined
656 anchors.right: parent.right
658 PropertyChanges { target: notifications; width: units.gu(38) }
666 objectName: "dialogs"
669 usageScenario: shell.usageScenario
671 shutdownFadeOutRectangle.enabled = true;
672 shutdownFadeOutRectangle.visible = true;
673 shutdownFadeOut.start();
678 target: SessionBroadcast
679 onShowHome: if (shell.mode !== "greeter") showHome()
684 objectName: "urlDispatcher"
685 active: shell.mode === "greeter"
686 onUrlRequested: shell.activateURL(url)
693 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
696 ignoreUnknownSignals: true
697 onItemSnapshotRequested: itemGrabber.capture(item)
702 id: cursorHidingTimer
704 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
705 onTriggered: cursor.opacity = 0;
711 visible: shell.hasMouse
713 topBoundaryOffset: panel.panelHeight
715 confiningItem: stage.itemConfiningMouseCursor
717 property bool mouseNeverMoved: true
719 target: cursor; property: "x"; value: shell.width / 2
720 when: cursor.mouseNeverMoved && cursor.visible
723 target: cursor; property: "y"; value: shell.height / 2
724 when: cursor.mouseNeverMoved && cursor.visible
729 readonly property var previewRectangle: stage.previewRectangle.target &&
730 stage.previewRectangle.target.dragging ?
731 stage.previewRectangle : null
733 onPushedLeftBoundary: {
734 if (buttons === Qt.NoButton) {
735 launcher.pushEdge(amount);
736 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
737 previewRectangle.maximizeLeft(amount);
741 onPushedRightBoundary: {
742 if (buttons === Qt.NoButton) {
743 stage.pushRightEdge(amount);
744 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
745 previewRectangle.maximizeRight(amount);
749 onPushedTopBoundary: {
750 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
751 previewRectangle.maximize(amount);
754 onPushedTopLeftCorner: {
755 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
756 previewRectangle.maximizeTopLeft(amount);
759 onPushedTopRightCorner: {
760 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
761 previewRectangle.maximizeTopRight(amount);
764 onPushedBottomLeftCorner: {
765 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
766 previewRectangle.maximizeBottomLeft(amount);
769 onPushedBottomRightCorner: {
770 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
771 previewRectangle.maximizeBottomRight(amount);
775 if (previewRectangle) {
776 previewRectangle.stop();
781 mouseNeverMoved = false;
785 Behavior on opacity { UbuntuNumberAnimation {} }
788 // non-visual objects
793 id: shutdownFadeOutRectangle
800 NumberAnimation on opacity {
805 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
806 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