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
282 applicationManager: ApplicationManager
283 topLevelSurfaceList: topLevelSurfaceList
284 inputMethodRect: inputMethod.visibleRect
286 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
288 : shell.usageScenario
290 mode: usageScenario == "phone" ? "staged"
291 : usageScenario == "tablet" ? "stagedWithSideStage"
294 shellOrientation: shell.orientation
295 shellOrientationAngle: shell.orientationAngle
296 orientations: shell.orientations
297 nativeWidth: shell.nativeWidth
298 nativeHeight: shell.nativeHeight
300 interactive: (!greeter || !greeter.shown)
301 && panel.indicators.fullyClosed
302 && !notifications.useModal
304 onInteractiveChanged: { if (interactive) { focus = true; } }
306 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
307 suspended: greeter.shown
308 altTabPressed: physicalKeysMapper.altTabPressed
309 oskEnabled: shell.oskEnabled
311 // TODO: This is not implemented yet in the new stage...
312 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
318 objectName: "inputMethod"
321 topMargin: panel.panelHeight
322 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
324 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || (greeter.shown && launcher.shown) ? overlay.z + 1 : overlay.z - 1
329 objectName: "greeterLoader"
331 anchors.topMargin: panel.panelHeight
332 sourceComponent: shell.mode != "shell" ? integratedGreeter :
333 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
335 item.objectName = "greeter"
340 id: integratedGreeter
343 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
344 hides: [launcher, panel.indicators]
345 tabletMode: shell.usageScenario != "phone"
346 forcedUnlock: wizard.active || shell.mode === "full-shell"
347 background: wallpaperResolver.cachedBackground
348 hasCustomBackground: wallpaperResolver.hasCustomBackground
349 allowFingerprint: !dialogs.hasActiveDialog &&
350 !notifications.topmostIsFullscreen &&
351 !panel.indicators.shown
353 // avoid overlapping with Launcher's edge drag area
354 // FIXME: Fix TouchRegistry & friends and remove this workaround
355 // Issue involves launcher's DDA getting disabled on a long
357 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
364 if (!tutorial.running) {
369 onEmergencyCall: startLockedApp("dialer-app")
374 // See powerConnection for why this is useful
375 id: showGreeterDelayed
387 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
388 // We just received an incoming call while locked. The
389 // indicator will have already launched dialer-app for us, but
390 // there is a race between "hasCalls" changing and the dialer
391 // starting up. So in case we lose that race, we'll start/
392 // focus the dialer ourselves here too. Even if the indicator
393 // didn't launch the dialer for some reason (or maybe a call
394 // started via some other means), if an active call is
395 // happening, we want to be in the dialer.
396 startLockedApp("dialer-app")
406 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
407 !callManager.hasCalls && !wizard.active) {
408 // We don't want to simply call greeter.showNow() here, because
409 // that will take too long. Qt will delay button event
410 // handling until the greeter is done loading and may think the
411 // user held down the power button the whole time, leading to a
412 // power dialog being shown. Instead, delay showing the
413 // greeter until we've finished handling the event. We could
414 // make the greeter load asynchronously instead, but that
415 // introduces a whole host of timing issues, especially with
416 // its animations. So this is simpler.
417 showGreeterDelayed.start();
422 function showHome() {
423 greeter.notifyUserRequestedApp();
425 if (shell.mode === "greeter") {
426 SessionBroadcast.requestHomeShown(AccountsService.user);
428 var animate = !LightDMService.greeter.active && !stages.shown;
429 dash.setCurrentScope(0, animate, false);
430 ApplicationManager.requestFocusApplication("unity8-dash");
443 anchors.fill: parent //because this draws indicator menus
446 available: tutorial.panelEnabled
447 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
448 && (!greeter || !greeter.hasLockedApp)
449 && !shell.waitingOnGreeter
450 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
452 minimizedPanelHeight: units.gu(3)
453 expandedPanelHeight: units.gu(7)
455 indicatorsModel: Indicators.IndicatorsModel {
456 // tablet and phone both use the same profile
457 // FIXME: use just "phone" for greeter too, but first fix
458 // greeter app launching to either load the app inside the
459 // greeter or tell the session to load the app. This will
460 // involve taking the url-dispatcher dbus name and using
461 // SessionBroadcast to tell the session.
462 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
463 Component.onCompleted: load();
468 greeterShown: greeter.shown
471 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
472 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
474 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
475 || greeter.hasLockedApp
476 locked: greeter && greeter.active
481 objectName: "launcher"
483 anchors.top: parent.top
484 anchors.topMargin: inverted ? 0 : panel.panelHeight
485 anchors.bottom: parent.bottom
487 dragAreaWidth: shell.edgeSize
488 available: tutorial.launcherEnabled
489 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
490 && !greeter.hasLockedApp
491 && !shell.waitingOnGreeter
492 && settings.enableLauncher
493 inverted: shell.usageScenario !== "desktop"
494 superPressed: physicalKeysMapper.superPressed
495 superTabPressed: physicalKeysMapper.superTabPressed
496 panelWidth: units.gu(settings.launcherWidth)
497 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
498 blurSource: greeter.shown ? greeter : stages
499 topPanelHeight: panel.panelHeight
501 onShowDashHome: showHome()
502 onLauncherApplicationSelected: {
503 greeter.notifyUserRequestedApp();
504 shell.activateApplication(appId);
508 panel.indicators.hide()
518 shortcut: Qt.MetaModifier | Qt.Key_A
520 launcher.openDrawer();
524 shortcut: Qt.AltModifier | Qt.Key_F1
526 launcher.openForKeyboardNavigation();
530 shortcut: Qt.MetaModifier | Qt.Key_0
532 if (LauncherModel.get(9)) {
533 activateApplication(LauncherModel.get(9).appId);
540 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
542 if (LauncherModel.get(index)) {
543 activateApplication(LauncherModel.get(index).appId);
550 KeyboardShortcutsOverlay {
551 objectName: "shortcutsOverlay"
552 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
553 && height < parent.height - padding - panel.panelHeight
554 anchors.centerIn: parent
555 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
556 anchors.verticalCenterOffset: panel.panelHeight/2
558 opacity: enabled ? 0.95 : 0
560 Behavior on opacity {
561 UbuntuNumberAnimation {}
567 objectName: "tutorial"
570 paused: callManager.hasCalls || !greeter || greeter.active ||
572 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
573 inputMethod.visible ||
574 (launcher.shown && !launcher.lockedVisible) ||
575 panel.indicators.shown || stage.rightEdgeDragProgress > 0
576 usageScenario: shell.usageScenario
577 lastInputTimestamp: inputFilter.lastInputTimestamp
587 deferred: shell.mode === "greeter"
589 function unlockWhenDoneWithWizard() {
591 Connectivity.unlockAllModems();
595 Component.onCompleted: unlockWhenDoneWithWizard()
596 onActiveChanged: unlockWhenDoneWithWizard()
599 MouseArea { // modal notifications prevent interacting with other contents
601 visible: notifications.useModal
608 model: NotificationBackend.Model
610 hasMouse: shell.hasMouse
611 background: wallpaperResolver.cachedBackground
613 y: topmostIsFullscreen ? 0 : panel.panelHeight
614 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
619 when: overlay.width <= units.gu(60)
621 target: notifications
622 anchors.left: parent.left
623 anchors.right: parent.right
628 when: overlay.width > units.gu(60)
630 target: notifications
631 anchors.left: undefined
632 anchors.right: parent.right
634 PropertyChanges { target: notifications; width: units.gu(38) }
642 objectName: "dialogs"
645 usageScenario: shell.usageScenario
647 shutdownFadeOutRectangle.enabled = true;
648 shutdownFadeOutRectangle.visible = true;
649 shutdownFadeOut.start();
654 target: SessionBroadcast
655 onShowHome: if (shell.mode !== "greeter") showHome()
660 objectName: "urlDispatcher"
661 active: shell.mode === "greeter"
662 onUrlRequested: shell.activateURL(url)
669 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
672 ignoreUnknownSignals: true
673 onItemSnapshotRequested: itemGrabber.capture(item)
678 id: cursorHidingTimer
680 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
681 onTriggered: cursor.opacity = 0;
687 visible: shell.hasMouse
689 topBoundaryOffset: panel.panelHeight
691 confiningItem: stage.itemConfiningMouseCursor
693 property bool mouseNeverMoved: true
695 target: cursor; property: "x"; value: shell.width / 2
696 when: cursor.mouseNeverMoved && cursor.visible
699 target: cursor; property: "y"; value: shell.height / 2
700 when: cursor.mouseNeverMoved && cursor.visible
705 readonly property var previewRectangle: stage.previewRectangle.target &&
706 stage.previewRectangle.target.dragging ?
707 stage.previewRectangle : null
709 onPushedLeftBoundary: {
710 if (buttons === Qt.NoButton) {
711 launcher.pushEdge(amount);
712 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
713 previewRectangle.maximizeLeft(amount);
717 onPushedRightBoundary: {
718 if (buttons === Qt.NoButton) {
719 stage.pushRightEdge(amount);
720 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
721 previewRectangle.maximizeRight(amount);
725 onPushedTopBoundary: {
726 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
727 previewRectangle.maximize(amount);
730 onPushedTopLeftCorner: {
731 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
732 previewRectangle.maximizeTopLeft(amount);
735 onPushedTopRightCorner: {
736 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
737 previewRectangle.maximizeTopRight(amount);
740 onPushedBottomLeftCorner: {
741 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
742 previewRectangle.maximizeBottomLeft(amount);
745 onPushedBottomRightCorner: {
746 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
747 previewRectangle.maximizeBottomRight(amount);
751 if (previewRectangle) {
752 previewRectangle.stop();
757 mouseNeverMoved = false;
761 Behavior on opacity { UbuntuNumberAnimation {} }
768 id: shutdownFadeOutRectangle
775 NumberAnimation on opacity {
780 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
781 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