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
48import Unity.Debug 0.1 as Debug
54 theme.name: "Ubuntu.Components.Themes.SuruDark"
56 // to be set from outside
57 property alias surfaceManager: topLevelSurfaceList.surfaceManager
58 property int orientationAngle: 0
59 property int orientation
60 property Orientations orientations
61 property real nativeWidth
62 property real nativeHeight
63 property alias panelAreaShowProgress: panel.panelAreaShowProgress
64 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
65 property string mode: "full-greeter"
66 property alias oskEnabled: inputMethod.enabled
67 function updateFocusedAppOrientation() {
68 stage.updateFocusedAppOrientation();
70 function updateFocusedAppOrientationAnimated() {
71 stage.updateFocusedAppOrientationAnimated();
73 property bool hasMouse: false
75 // to be read from outside
76 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
78 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
79 && stage.orientationChangesEnabled
80 && (!greeter || !greeter.animating)
82 readonly property bool showingGreeter: greeter && greeter.shown
84 property bool startingUp: true
85 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
87 property int supportedOrientations: {
89 // Ensure we don't rotate during start up
90 return Qt.PrimaryOrientation;
91 } else if (showingGreeter || notifications.topmostIsFullscreen) {
92 return Qt.PrimaryOrientation;
94 return shell.orientations.map(stage.supportedOrientations);
98 readonly property var mainApp: stage.mainApp
102 _onMainAppChanged(mainApp.appId);
106 target: ApplicationManager
108 if (shell.mainApp && shell.mainApp.appId === appId) {
109 _onMainAppChanged(appId);
113 function _onMainAppChanged(appId) {
114 if (wizard.active && appId != "" && appId != "unity8-dash") {
115 // If this happens on first boot, we may be in the
116 // wizard while receiving a call. But a call is more
117 // important than the wizard so just bail out of it.
121 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
122 // If we are in the middle of a call, make dialer lockedApp and show it.
123 // This can happen if user backs out of dialer back to greeter, then
124 // launches dialer again.
125 greeter.lockedApp = appId;
127 greeter.notifyAppFocusRequested(appId);
129 panel.indicators.hide();
130 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
133 // For autopilot consumption
134 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
136 // Note when greeter is waiting on PAM, so that we can disable edges until
137 // we know which user data to show and whether the session is locked.
138 readonly property bool waitingOnGreeter: greeter && greeter.waiting
140 property real edgeSize: units.gu(settings.edgeDragWidth)
143 id: wallpaperResolver
144 objectName: "wallpaperResolver"
146 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
147 readonly property bool hasCustomBackground: background != defaultBackground
149 // Use a cached version of the scaled-down wallpaper (as sometimes the
150 // image can be quite big compared to the device size, including for
151 // our default wallpaper). We use a name=wallpaper argument here to
152 // make sure we don't litter our cache with lots of scaled images. We
153 // only need to bother caching one at a time.
154 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
157 id: backgroundSettings
158 schema.id: "org.gnome.desktop.background"
162 AccountsService.backgroundFile,
163 backgroundSettings.pictureUri,
168 readonly property alias greeter: greeterLoader.item
170 function activateApplication(appId) {
171 // Either open the app in our own session, or -- if we're acting as a
172 // greeter -- ask the user's session to open it for us.
173 if (shell.mode === "greeter") {
174 activateURL("application:///" + appId + ".desktop");
180 function activateURL(url) {
181 SessionBroadcast.requestUrlStart(AccountsService.user, url);
182 greeter.notifyUserRequestedApp();
183 panel.indicators.hide();
186 function startApp(appId) {
187 if (ApplicationManager.findApplication(appId)) {
188 ApplicationManager.requestFocusApplication(appId);
190 ApplicationManager.startApplication(appId);
194 function startLockedApp(app) {
195 if (greeter.locked) {
196 greeter.lockedApp = app;
198 startApp(app); // locked apps are always in our same session
202 target: LauncherModel
203 property: "applicationManager"
204 value: ApplicationManager
207 Component.onCompleted: {
208 finishStartUpTimer.start();
217 objectName: "dashCommunicator"
221 id: physicalKeysMapper
222 objectName: "physicalKeysMapper"
224 onPowerKeyLongPressed: dialogs.showPowerDialog();
225 onVolumeDownTriggered: volumeControl.volumeDown();
226 onVolumeUpTriggered: volumeControl.volumeUp();
227 onScreenshotTriggered: itemGrabber.capture(shell);
231 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
236 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
237 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
241 objectName: "windowInputMonitor"
242 onHomeKeyActivated: {
243 // Ignore when greeter is active, to avoid pocket presses
244 if (!greeter.active) {
249 onTouchBegun: { cursor.opacity = 0; }
251 // move the (hidden) cursor to the last known touch position
252 var mappedCoords = mapFromItem(null, pos.x, pos.y);
253 cursor.x = mappedCoords.x;
254 cursor.y = mappedCoords.y;
255 cursor.mouseNeverMoved = false;
261 schema.id: "com.canonical.Unity8"
266 objectName: "panelState"
273 height: parent.height
275 TopLevelWindowModel {
276 id: topLevelSurfaceList
277 objectName: "topLevelSurfaceList"
278 applicationManager: ApplicationManager // it's a singleton
287 dragAreaWidth: shell.edgeSize
288 background: wallpaperResolver.background
290 applicationManager: ApplicationManager
291 topLevelSurfaceList: topLevelSurfaceList
292 inputMethodRect: inputMethod.visibleRect
294 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
296 : shell.usageScenario
298 mode: usageScenario == "phone" ? "staged"
299 : usageScenario == "tablet" ? "stagedWithSideStage"
302 shellOrientation: shell.orientation
303 shellOrientationAngle: shell.orientationAngle
304 orientations: shell.orientations
305 nativeWidth: shell.nativeWidth
306 nativeHeight: shell.nativeHeight
308 interactive: (!greeter || !greeter.shown)
309 && panel.indicators.fullyClosed
310 && !notifications.useModal
312 onInteractiveChanged: { if (interactive) { focus = true; } }
314 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
315 suspended: greeter.shown
316 altTabPressed: physicalKeysMapper.altTabPressed
317 oskEnabled: shell.oskEnabled
318 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
319 panelState: panelState
321 onSpreadShownChanged: {
322 panel.indicators.hide();
323 panel.applicationMenus.hide();
326 target: applicationsDisplayLoader.item
327 property: "oskEnabled"
328 value: shell.oskEnabled
335 objectName: "inputMethod"
336 surface: topLevelSurfaceList.inputMethodSurface
339 topMargin: panel.panelHeight
340 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
342 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
347 objectName: "greeterLoader"
349 anchors.topMargin: panel.panelHeight
351 if (shell.mode != "shell") {
352 if (screenWindow.primary) return integratedGreeter;
353 return secondaryGreeter;
355 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
358 item.objectName = "greeter"
363 id: integratedGreeter
366 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
367 hides: [launcher, panel.indicators, panel.applicationMenus]
368 tabletMode: shell.usageScenario != "phone"
369 forcedUnlock: wizard.active || shell.mode === "full-shell"
370 background: wallpaperResolver.cachedBackground
371 hasCustomBackground: wallpaperResolver.hasCustomBackground
372 allowFingerprint: !dialogs.hasActiveDialog &&
373 !notifications.topmostIsFullscreen &&
374 !panel.indicators.shown
376 // avoid overlapping with Launcher's edge drag area
377 // FIXME: Fix TouchRegistry & friends and remove this workaround
378 // Issue involves launcher's DDA getting disabled on a long
380 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
387 if (!tutorial.running) {
392 onEmergencyCall: startLockedApp("dialer-app")
399 hides: [launcher, panel.indicators]
404 // See powerConnection for why this is useful
405 id: showGreeterDelayed
417 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
418 // We just received an incoming call while locked. The
419 // indicator will have already launched dialer-app for us, but
420 // there is a race between "hasCalls" changing and the dialer
421 // starting up. So in case we lose that race, we'll start/
422 // focus the dialer ourselves here too. Even if the indicator
423 // didn't launch the dialer for some reason (or maybe a call
424 // started via some other means), if an active call is
425 // happening, we want to be in the dialer.
426 startLockedApp("dialer-app")
436 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
437 !callManager.hasCalls && !wizard.active) {
438 // We don't want to simply call greeter.showNow() here, because
439 // that will take too long. Qt will delay button event
440 // handling until the greeter is done loading and may think the
441 // user held down the power button the whole time, leading to a
442 // power dialog being shown. Instead, delay showing the
443 // greeter until we've finished handling the event. We could
444 // make the greeter load asynchronously instead, but that
445 // introduces a whole host of timing issues, especially with
446 // its animations. So this is simpler.
447 showGreeterDelayed.start();
452 function showHome() {
453 greeter.notifyUserRequestedApp();
455 if (shell.mode === "greeter") {
456 SessionBroadcast.requestHomeShown(AccountsService.user);
458 var animate = !LightDMService.greeter.active && !stages.shown;
459 dash.setCurrentScope(0, animate, false);
460 ApplicationManager.requestFocusApplication("unity8-dash");
473 anchors.fill: parent //because this draws indicator menus
475 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
476 minimizedPanelHeight: units.gu(3)
477 expandedPanelHeight: units.gu(7)
478 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
479 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
483 available: tutorial.panelEnabled
484 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
485 && (!greeter || !greeter.hasLockedApp)
486 && !shell.waitingOnGreeter
487 && settings.enableIndicatorMenu
489 model: Indicators.IndicatorsModel {
490 // tablet and phone both use the same profile
491 // FIXME: use just "phone" for greeter too, but first fix
492 // greeter app launching to either load the app inside the
493 // greeter or tell the session to load the app. This will
494 // involve taking the url-dispatcher dbus name and using
495 // SessionBroadcast to tell the session.
496 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
497 Component.onCompleted: load();
503 available: (!greeter || !greeter.shown)
504 && !shell.waitingOnGreeter
507 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
508 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
510 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
511 || greeter.hasLockedApp
512 greeterShown: greeter && greeter.shown
513 panelState: panelState
518 objectName: "launcher"
520 anchors.top: parent.top
521 anchors.topMargin: inverted ? 0 : panel.panelHeight
522 anchors.bottom: parent.bottom
524 dragAreaWidth: shell.edgeSize
525 available: tutorial.launcherEnabled
526 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
527 && !greeter.hasLockedApp
528 && !shell.waitingOnGreeter
529 && settings.enableLauncher
530 inverted: shell.usageScenario !== "desktop"
531 superPressed: physicalKeysMapper.superPressed
532 superTabPressed: physicalKeysMapper.superTabPressed
533 panelWidth: units.gu(settings.launcherWidth)
534 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
535 blurSource: greeter.shown ? greeter : stages
536 topPanelHeight: panel.panelHeight
537 drawerEnabled: !greeter.shown
539 onShowDashHome: showHome()
540 onLauncherApplicationSelected: {
541 greeter.notifyUserRequestedApp();
542 shell.activateApplication(appId);
546 panel.indicators.hide()
556 shortcut: Qt.MetaModifier | Qt.Key_A
558 launcher.openDrawer(true);
562 shortcut: Qt.AltModifier | Qt.Key_F1
564 launcher.openForKeyboardNavigation();
568 shortcut: Qt.MetaModifier | Qt.Key_0
570 if (LauncherModel.get(9)) {
571 activateApplication(LauncherModel.get(9).appId);
578 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
580 if (LauncherModel.get(index)) {
581 activateApplication(LauncherModel.get(index).appId);
588 KeyboardShortcutsOverlay {
589 objectName: "shortcutsOverlay"
590 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
591 && height < parent.height - padding - panel.panelHeight
592 anchors.centerIn: parent
593 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
594 anchors.verticalCenterOffset: panel.panelHeight/2
596 opacity: enabled ? 0.95 : 0
598 Behavior on opacity {
599 UbuntuNumberAnimation {}
605 objectName: "tutorial"
608 paused: callManager.hasCalls || !greeter || greeter.active ||
610 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
611 inputMethod.visible ||
612 (launcher.shown && !launcher.lockedVisible) ||
613 panel.indicators.shown || stage.rightEdgeDragProgress > 0
614 usageScenario: shell.usageScenario
615 lastInputTimestamp: inputFilter.lastInputTimestamp
625 deferred: shell.mode === "greeter"
627 function unlockWhenDoneWithWizard() {
629 Connectivity.unlockAllModems();
633 Component.onCompleted: unlockWhenDoneWithWizard()
634 onActiveChanged: unlockWhenDoneWithWizard()
637 MouseArea { // modal notifications prevent interacting with other contents
639 visible: notifications.useModal
646 model: NotificationBackend.Model
648 hasMouse: shell.hasMouse
649 background: wallpaperResolver.cachedBackground
651 y: topmostIsFullscreen ? 0 : panel.panelHeight
652 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
657 when: overlay.width <= units.gu(60)
659 target: notifications
660 anchors.left: parent.left
661 anchors.right: parent.right
666 when: overlay.width > units.gu(60)
668 target: notifications
669 anchors.left: undefined
670 anchors.right: parent.right
672 PropertyChanges { target: notifications; width: units.gu(38) }
680 objectName: "dialogs"
683 usageScenario: shell.usageScenario
685 shutdownFadeOutRectangle.enabled = true;
686 shutdownFadeOutRectangle.visible = true;
687 shutdownFadeOut.start();
692 target: SessionBroadcast
693 onShowHome: if (shell.mode !== "greeter") showHome()
698 objectName: "urlDispatcher"
699 active: shell.mode === "greeter"
700 onUrlRequested: shell.activateURL(url)
707 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
710 ignoreUnknownSignals: true
711 onItemSnapshotRequested: itemGrabber.capture(item)
716 id: cursorHidingTimer
718 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
719 onTriggered: cursor.opacity = 0;
724 objectName: "Pointer-"+screenWindow.objectName
728 topBoundaryOffset: panel.panelHeight
729 enabled: shell.hasMouse && screenWindow.active
732 property bool mouseNeverMoved: true
734 target: cursor; property: "x"; value: shell.width / 2
735 when: cursor.mouseNeverMoved && cursor.visible
738 target: cursor; property: "y"; value: shell.height / 2
739 when: cursor.mouseNeverMoved && cursor.visible
742 confiningItem: stage.itemConfiningMouseCursor
746 readonly property var previewRectangle: stage.previewRectangle.target &&
747 stage.previewRectangle.target.dragging ?
748 stage.previewRectangle : null
750 onPushedLeftBoundary: {
751 if (buttons === Qt.NoButton) {
752 launcher.pushEdge(amount);
753 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
754 previewRectangle.maximizeLeft(amount);
758 onPushedRightBoundary: {
759 if (buttons === Qt.NoButton) {
760 stage.pushRightEdge(amount);
761 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
762 previewRectangle.maximizeRight(amount);
766 onPushedTopBoundary: {
767 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
768 previewRectangle.maximize(amount);
771 onPushedTopLeftCorner: {
772 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
773 previewRectangle.maximizeTopLeft(amount);
776 onPushedTopRightCorner: {
777 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
778 previewRectangle.maximizeTopRight(amount);
781 onPushedBottomLeftCorner: {
782 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
783 previewRectangle.maximizeBottomLeft(amount);
786 onPushedBottomRightCorner: {
787 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
788 previewRectangle.maximizeBottomRight(amount);
792 if (previewRectangle) {
793 previewRectangle.stop();
798 mouseNeverMoved = false;
802 Behavior on opacity { UbuntuNumberAnimation {} }
805 // non-visual objects
807 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
812 id: shutdownFadeOutRectangle
819 NumberAnimation on opacity {
824 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
825 DBusUnitySessionService.shutdown();
832 z: shutdownFadeOutRectangle.z + 1
833 active: DebuggingController.logOverlay
835 sourceComponent: Text {
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