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 "Components/PanelState"
42import Unity.Notifications 1.0 as NotificationBackend
43import Unity.Session 0.1
44import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
47import WindowManager 1.0
53 theme.name: "Ubuntu.Components.Themes.SuruDark"
55 // to be set from outside
56 property alias surfaceManager: topLevelSurfaceList.surfaceManager
57 property int orientationAngle: 0
58 property int orientation
59 property Orientations orientations
60 property real nativeWidth
61 property real nativeHeight
62 property alias panelAreaShowProgress: panel.panelAreaShowProgress
63 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
64 property string mode: "full-greeter"
65 property alias oskEnabled: inputMethod.enabled
66 function updateFocusedAppOrientation() {
67 stage.updateFocusedAppOrientation();
69 function updateFocusedAppOrientationAnimated() {
70 stage.updateFocusedAppOrientationAnimated();
72 property bool hasMouse: 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) {
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;
259 schema.id: "com.canonical.Unity8"
264 objectName: "panelState"
271 height: parent.height
273 TopLevelWindowModel {
274 id: topLevelSurfaceList
275 objectName: "topLevelSurfaceList"
276 applicationManager: ApplicationManager // it's a singleton
285 dragAreaWidth: shell.edgeSize
286 background: wallpaperResolver.background
288 applicationManager: ApplicationManager
289 topLevelSurfaceList: topLevelSurfaceList
290 inputMethodRect: inputMethod.visibleRect
292 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
294 : shell.usageScenario
296 mode: usageScenario == "phone" ? "staged"
297 : usageScenario == "tablet" ? "stagedWithSideStage"
300 shellOrientation: shell.orientation
301 shellOrientationAngle: shell.orientationAngle
302 orientations: shell.orientations
303 nativeWidth: shell.nativeWidth
304 nativeHeight: shell.nativeHeight
306 interactive: (!greeter || !greeter.shown)
307 && panel.indicators.fullyClosed
308 && !notifications.useModal
310 onInteractiveChanged: { if (interactive) { focus = true; } }
312 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
313 suspended: greeter.shown
314 altTabPressed: physicalKeysMapper.altTabPressed
315 oskEnabled: shell.oskEnabled
316 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
317 panelState: panelState
319 onSpreadShownChanged: {
320 panel.indicators.hide();
321 panel.applicationMenus.hide();
328 objectName: "inputMethod"
329 surface: topLevelSurfaceList.inputMethodSurface
332 topMargin: panel.panelHeight
333 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
335 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
340 objectName: "greeterLoader"
342 anchors.topMargin: panel.panelHeight
344 if (shell.mode != "shell") {
345 if (screenWindow.primary) return integratedGreeter;
346 return secondaryGreeter;
348 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
351 item.objectName = "greeter"
356 id: integratedGreeter
359 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
360 hides: [launcher, panel.indicators, panel.applicationMenus]
361 tabletMode: shell.usageScenario != "phone"
362 forcedUnlock: wizard.active || shell.mode === "full-shell"
363 background: wallpaperResolver.cachedBackground
364 hasCustomBackground: wallpaperResolver.hasCustomBackground
365 allowFingerprint: !dialogs.hasActiveDialog &&
366 !notifications.topmostIsFullscreen &&
367 !panel.indicators.shown
369 // avoid overlapping with Launcher's edge drag area
370 // FIXME: Fix TouchRegistry & friends and remove this workaround
371 // Issue involves launcher's DDA getting disabled on a long
373 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
380 if (!tutorial.running) {
385 onEmergencyCall: startLockedApp("dialer-app")
392 hides: [launcher, panel.indicators]
397 // See powerConnection for why this is useful
398 id: showGreeterDelayed
410 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
411 // We just received an incoming call while locked. The
412 // indicator will have already launched dialer-app for us, but
413 // there is a race between "hasCalls" changing and the dialer
414 // starting up. So in case we lose that race, we'll start/
415 // focus the dialer ourselves here too. Even if the indicator
416 // didn't launch the dialer for some reason (or maybe a call
417 // started via some other means), if an active call is
418 // happening, we want to be in the dialer.
419 startLockedApp("dialer-app")
429 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
430 !callManager.hasCalls && !wizard.active) {
431 // We don't want to simply call greeter.showNow() here, because
432 // that will take too long. Qt will delay button event
433 // handling until the greeter is done loading and may think the
434 // user held down the power button the whole time, leading to a
435 // power dialog being shown. Instead, delay showing the
436 // greeter until we've finished handling the event. We could
437 // make the greeter load asynchronously instead, but that
438 // introduces a whole host of timing issues, especially with
439 // its animations. So this is simpler.
440 showGreeterDelayed.start();
445 function showHome() {
446 greeter.notifyUserRequestedApp();
448 if (shell.mode === "greeter") {
449 SessionBroadcast.requestHomeShown(AccountsService.user);
451 var animate = !LightDMService.greeter.active && !stages.shown;
452 dash.setCurrentScope(0, animate, false);
453 ApplicationManager.requestFocusApplication("unity8-dash");
466 anchors.fill: parent //because this draws indicator menus
468 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
469 minimizedPanelHeight: units.gu(3)
470 expandedPanelHeight: units.gu(7)
471 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
472 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
476 available: tutorial.panelEnabled
477 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
478 && (!greeter || !greeter.hasLockedApp)
479 && !shell.waitingOnGreeter
480 && settings.enableIndicatorMenu
482 model: Indicators.IndicatorsModel {
483 // tablet and phone both use the same profile
484 // FIXME: use just "phone" for greeter too, but first fix
485 // greeter app launching to either load the app inside the
486 // greeter or tell the session to load the app. This will
487 // involve taking the url-dispatcher dbus name and using
488 // SessionBroadcast to tell the session.
489 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
490 Component.onCompleted: load();
496 available: (!greeter || !greeter.shown)
497 && !shell.waitingOnGreeter
500 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
501 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
503 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
504 || greeter.hasLockedApp
505 greeterShown: greeter && greeter.shown
506 panelState: panelState
511 objectName: "launcher"
513 anchors.top: parent.top
514 anchors.topMargin: inverted ? 0 : panel.panelHeight
515 anchors.bottom: parent.bottom
517 dragAreaWidth: shell.edgeSize
518 available: tutorial.launcherEnabled
519 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
520 && !greeter.hasLockedApp
521 && !shell.waitingOnGreeter
522 && settings.enableLauncher
523 inverted: shell.usageScenario !== "desktop"
524 superPressed: physicalKeysMapper.superPressed
525 superTabPressed: physicalKeysMapper.superTabPressed
526 panelWidth: units.gu(settings.launcherWidth)
527 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
528 blurSource: greeter.shown ? greeter : stages
529 topPanelHeight: panel.panelHeight
530 drawerEnabled: !greeter.shown
532 onShowDashHome: showHome()
533 onLauncherApplicationSelected: {
534 greeter.notifyUserRequestedApp();
535 shell.activateApplication(appId);
539 panel.indicators.hide()
549 shortcut: Qt.MetaModifier | Qt.Key_A
551 launcher.openDrawer(true);
555 shortcut: Qt.AltModifier | Qt.Key_F1
557 launcher.openForKeyboardNavigation();
561 shortcut: Qt.MetaModifier | Qt.Key_0
563 if (LauncherModel.get(9)) {
564 activateApplication(LauncherModel.get(9).appId);
571 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
573 if (LauncherModel.get(index)) {
574 activateApplication(LauncherModel.get(index).appId);
581 KeyboardShortcutsOverlay {
582 objectName: "shortcutsOverlay"
583 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
584 && height < parent.height - padding - panel.panelHeight
585 anchors.centerIn: parent
586 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
587 anchors.verticalCenterOffset: panel.panelHeight/2
589 opacity: enabled ? 0.95 : 0
591 Behavior on opacity {
592 UbuntuNumberAnimation {}
598 objectName: "tutorial"
601 paused: callManager.hasCalls || !greeter || greeter.active ||
603 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
604 inputMethod.visible ||
605 (launcher.shown && !launcher.lockedVisible) ||
606 panel.indicators.shown || stage.rightEdgeDragProgress > 0
607 usageScenario: shell.usageScenario
608 lastInputTimestamp: inputFilter.lastInputTimestamp
618 deferred: shell.mode === "greeter"
620 function unlockWhenDoneWithWizard() {
622 Connectivity.unlockAllModems();
626 Component.onCompleted: unlockWhenDoneWithWizard()
627 onActiveChanged: unlockWhenDoneWithWizard()
630 MouseArea { // modal notifications prevent interacting with other contents
632 visible: notifications.useModal
639 model: NotificationBackend.Model
641 hasMouse: shell.hasMouse
642 background: wallpaperResolver.cachedBackground
644 y: topmostIsFullscreen ? 0 : panel.panelHeight
645 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
650 when: overlay.width <= units.gu(60)
652 target: notifications
653 anchors.left: parent.left
654 anchors.right: parent.right
659 when: overlay.width > units.gu(60)
661 target: notifications
662 anchors.left: undefined
663 anchors.right: parent.right
665 PropertyChanges { target: notifications; width: units.gu(38) }
673 objectName: "dialogs"
676 usageScenario: shell.usageScenario
678 shutdownFadeOutRectangle.enabled = true;
679 shutdownFadeOutRectangle.visible = true;
680 shutdownFadeOut.start();
685 target: SessionBroadcast
686 onShowHome: if (shell.mode !== "greeter") showHome()
691 objectName: "urlDispatcher"
692 active: shell.mode === "greeter"
693 onUrlRequested: shell.activateURL(url)
700 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
703 ignoreUnknownSignals: true
704 onItemSnapshotRequested: itemGrabber.capture(item)
709 id: cursorHidingTimer
711 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
712 onTriggered: cursor.opacity = 0;
717 visible: shell.hasMouse
720 topBoundaryOffset: panel.panelHeight
722 confiningItem: stage.itemConfiningMouseCursor
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();
781 Behavior on opacity { UbuntuNumberAnimation {} }
784 // non-visual objects
786 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
791 id: shutdownFadeOutRectangle
798 NumberAnimation on opacity {
803 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
804 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