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 bool beingResized
62 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
63 property string mode: "full-greeter"
64 property alias oskEnabled: inputMethod.enabled
65 function updateFocusedAppOrientation() {
66 stage.updateFocusedAppOrientation();
68 function updateFocusedAppOrientationAnimated() {
69 stage.updateFocusedAppOrientationAnimated();
71 property bool hasMouse: 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 != "" && appId != "unity8-dash") {
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();
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:///usr/share/backgrounds/warty-final-ubuntu.png"
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();
215 objectName: "dashCommunicator"
219 id: physicalKeysMapper
220 objectName: "physicalKeysMapper"
222 onPowerKeyLongPressed: dialogs.showPowerDialog();
223 onVolumeDownTriggered: volumeControl.volumeDown();
224 onVolumeUpTriggered: volumeControl.volumeUp();
225 onScreenshotTriggered: itemGrabber.capture(shell);
229 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
234 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
235 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
239 objectName: "windowInputMonitor"
240 onHomeKeyActivated: {
241 // Ignore when greeter is active, to avoid pocket presses
242 if (!greeter.active) {
247 onTouchBegun: { cursor.opacity = 0; }
249 // move the (hidden) cursor to the last known touch position
250 var mappedCoords = mapFromItem(null, pos.x, pos.y);
251 cursor.x = mappedCoords.x;
252 cursor.y = mappedCoords.y;
253 cursor.mouseNeverMoved = false;
259 schema.id: "com.canonical.Unity8"
266 height: parent.height
268 TopLevelSurfaceList {
269 id: topLevelSurfaceList
270 objectName: "topLevelSurfaceList"
271 applicationsModel: ApplicationManager
280 dragAreaWidth: shell.edgeSize
281 background: wallpaperResolver.background
282 leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
283 Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
285 applicationManager: ApplicationManager
286 topLevelSurfaceList: topLevelSurfaceList
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
301 // TODO: Is this still needed? Didn't come across it. Needs checking before merging
302 beingResized: shell.beingResized
304 // TODO: Is this still needed? Didn't come across it. Needs checking before merging
305 // Not just using panel.panelHeight as that changes depending on the focused app.
306 maximizedAppTopMargin: panel.indicators.minimizedPanelHeight
308 interactive: (!greeter || !greeter.shown)
309 && panel.indicators.fullyClosed
310 && launcher.progress == 0
311 && !notifications.useModal
313 onInteractiveChanged: { if (interactive) { focus = true; } }
315 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
316 suspended: greeter.shown
317 keepDashRunning: launcher.shown || launcher.dashSwipe
318 altTabPressed: physicalKeysMapper.altTabPressed
319 oskEnabled: shell.oskEnabled
321 // TODO: This is not implemented yet in the new stage...
322 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
328 objectName: "inputMethod"
331 topMargin: panel.panelHeight
332 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
334 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
339 objectName: "greeterLoader"
341 anchors.topMargin: panel.panelHeight
342 sourceComponent: shell.mode != "shell" ? integratedGreeter :
343 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
345 item.objectName = "greeter"
350 id: integratedGreeter
353 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
354 hides: [launcher, panel.indicators]
355 tabletMode: shell.usageScenario != "phone"
356 launcherOffset: launcher.progress
357 forcedUnlock: wizard.active || shell.mode === "full-shell"
358 background: wallpaperResolver.cachedBackground
359 hasCustomBackground: wallpaperResolver.hasCustomBackground
360 allowFingerprint: !dialogs.hasActiveDialog &&
361 !notifications.topmostIsFullscreen &&
362 !panel.indicators.shown
364 // avoid overlapping with Launcher's edge drag area
365 // FIXME: Fix TouchRegistry & friends and remove this workaround
366 // Issue involves launcher's DDA getting disabled on a long
368 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
375 if (!tutorial.running) {
380 onEmergencyCall: startLockedApp("dialer-app")
385 // See powerConnection for why this is useful
386 id: showGreeterDelayed
398 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
399 // We just received an incoming call while locked. The
400 // indicator will have already launched dialer-app for us, but
401 // there is a race between "hasCalls" changing and the dialer
402 // starting up. So in case we lose that race, we'll start/
403 // focus the dialer ourselves here too. Even if the indicator
404 // didn't launch the dialer for some reason (or maybe a call
405 // started via some other means), if an active call is
406 // happening, we want to be in the dialer.
407 startLockedApp("dialer-app")
417 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
418 !callManager.hasCalls && !wizard.active) {
419 // We don't want to simply call greeter.showNow() here, because
420 // that will take too long. Qt will delay button event
421 // handling until the greeter is done loading and may think the
422 // user held down the power button the whole time, leading to a
423 // power dialog being shown. Instead, delay showing the
424 // greeter until we've finished handling the event. We could
425 // make the greeter load asynchronously instead, but that
426 // introduces a whole host of timing issues, especially with
427 // its animations. So this is simpler.
428 showGreeterDelayed.start();
433 function showHome() {
434 greeter.notifyUserRequestedApp();
436 if (shell.mode === "greeter") {
437 SessionBroadcast.requestHomeShown(AccountsService.user);
439 var animate = !LightDMService.greeter.active && !stages.shown;
440 dash.setCurrentScope(0, animate, false);
441 ApplicationManager.requestFocusApplication("unity8-dash");
445 function showDash() {
446 if (greeter.notifyShowingDashFromDrag()) {
450 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
451 && ApplicationManager.focusedApplicationId != "unity8-dash") {
452 ApplicationManager.requestFocusApplication("unity8-dash")
466 anchors.fill: parent //because this draws indicator menus
469 available: tutorial.panelEnabled
470 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
471 && (!greeter || !greeter.hasLockedApp)
472 && !shell.waitingOnGreeter
473 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
475 minimizedPanelHeight: units.gu(3)
476 expandedPanelHeight: units.gu(7)
478 indicatorsModel: Indicators.IndicatorsModel {
479 // tablet and phone both use the same profile
480 // FIXME: use just "phone" for greeter too, but first fix
481 // greeter app launching to either load the app inside the
482 // greeter or tell the session to load the app. This will
483 // involve taking the url-dispatcher dbus name and using
484 // SessionBroadcast to tell the session.
485 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
486 Component.onCompleted: load();
491 greeterShown: greeter.shown
494 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
495 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
497 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
498 || greeter.hasLockedApp
499 locked: greeter && greeter.active
504 objectName: "launcher"
507 * Since the Dash doesn't have the same controll over surfaces that the
508 * Shell does, it can't slowly move the scope out of the way, as the shell
509 * does with apps, and the dash is show instantly. This allows for some
510 * leeway and prevents accidental home swipes.
512 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
513 readonly property bool dashSwipe: progress > offset
515 anchors.top: parent.top
516 anchors.topMargin: inverted ? 0 : panel.panelHeight
517 anchors.bottom: parent.bottom
519 dragAreaWidth: shell.edgeSize
520 available: tutorial.launcherEnabled
521 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
522 && !greeter.hasLockedApp
523 && !shell.waitingOnGreeter
524 inverted: shell.usageScenario !== "desktop"
525 superPressed: physicalKeysMapper.superPressed
526 superTabPressed: physicalKeysMapper.superTabPressed
527 panelWidth: units.gu(settings.launcherWidth)
528 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
530 onShowDashHome: showHome()
532 onDashSwipeChanged: {
534 dash.setCurrentScope(0, false, true)
537 onLauncherApplicationSelected: {
538 greeter.notifyUserRequestedApp();
539 shell.activateApplication(appId);
543 panel.indicators.hide()
553 shortcut: Qt.AltModifier | Qt.Key_F1
555 launcher.openForKeyboardNavigation();
559 shortcut: Qt.MetaModifier | Qt.Key_0
561 if (LauncherModel.get(9)) {
562 activateApplication(LauncherModel.get(9).appId);
569 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
571 if (LauncherModel.get(index)) {
572 activateApplication(LauncherModel.get(index).appId);
579 KeyboardShortcutsOverlay {
580 objectName: "shortcutsOverlay"
581 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
582 && height < parent.height - padding - panel.panelHeight
583 anchors.centerIn: parent
584 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
585 anchors.verticalCenterOffset: panel.panelHeight/2
587 opacity: enabled ? 0.95 : 0
589 Behavior on opacity {
590 UbuntuNumberAnimation {}
596 objectName: "tutorial"
599 paused: callManager.hasCalls || !greeter || greeter.active ||
601 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
602 inputMethod.visible ||
603 (launcher.shown && !launcher.lockedVisible) ||
604 panel.indicators.shown || stage.rightEdgeDragProgress > 0
605 usageScenario: shell.usageScenario
606 lastInputTimestamp: inputFilter.lastInputTimestamp
616 deferred: shell.mode === "greeter"
618 function unlockWhenDoneWithWizard() {
620 Connectivity.unlockAllModems();
624 Component.onCompleted: unlockWhenDoneWithWizard()
625 onActiveChanged: unlockWhenDoneWithWizard()
628 MouseArea { // modal notifications prevent interacting with other contents
630 visible: notifications.useModal
637 model: NotificationBackend.Model
639 hasMouse: shell.hasMouse
640 background: wallpaperResolver.cachedBackground
642 y: topmostIsFullscreen ? 0 : panel.panelHeight
643 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
648 when: overlay.width <= units.gu(60)
650 target: notifications
651 anchors.left: parent.left
652 anchors.right: parent.right
657 when: overlay.width > units.gu(60)
659 target: notifications
660 anchors.left: undefined
661 anchors.right: parent.right
663 PropertyChanges { target: notifications; width: units.gu(38) }
671 objectName: "dialogs"
674 usageScenario: shell.usageScenario
676 shutdownFadeOutRectangle.enabled = true;
677 shutdownFadeOutRectangle.visible = true;
678 shutdownFadeOut.start();
683 target: SessionBroadcast
684 onShowHome: if (shell.mode !== "greeter") showHome()
689 objectName: "urlDispatcher"
690 active: shell.mode === "greeter"
691 onUrlRequested: shell.activateURL(url)
698 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
701 ignoreUnknownSignals: true
702 onItemSnapshotRequested: itemGrabber.capture(item)
708 visible: shell.hasMouse
710 topBoundaryOffset: panel.panelHeight
712 confiningItem: stage.itemConfiningMouseCursor
714 property bool mouseNeverMoved: true
716 target: cursor; property: "x"; value: shell.width / 2
717 when: cursor.mouseNeverMoved && cursor.visible
720 target: cursor; property: "y"; value: shell.height / 2
721 when: cursor.mouseNeverMoved && cursor.visible
726 readonly property var previewRectangle: stage.previewRectangle.target &&
727 stage.previewRectangle.target.dragging ?
728 stage.previewRectangle : null
730 onPushedLeftBoundary: {
731 if (buttons === Qt.NoButton) {
732 launcher.pushEdge(amount);
733 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
734 previewRectangle.maximizeLeft(amount);
738 onPushedRightBoundary: {
739 if (buttons === Qt.NoButton) {
740 stage.pushRightEdge(amount);
741 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
742 previewRectangle.maximizeRight(amount);
746 onPushedTopBoundary: {
747 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
748 previewRectangle.maximize(amount);
751 onPushedTopLeftCorner: {
752 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
753 previewRectangle.maximizeTopLeft(amount);
756 onPushedTopRightCorner: {
757 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
758 previewRectangle.maximizeTopRight(amount);
761 onPushedBottomLeftCorner: {
762 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
763 previewRectangle.maximizeBottomLeft(amount);
766 onPushedBottomRightCorner: {
767 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
768 previewRectangle.maximizeBottomRight(amount);
772 if (previewRectangle) {
773 previewRectangle.stop();
778 mouseNeverMoved = false;
787 id: shutdownFadeOutRectangle
794 NumberAnimation on opacity {
799 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
800 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