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 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
73 property bool hasKeyboard: false
74 property bool hasTouchscreen: false
76 // to be read from outside
77 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
79 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
80 && stage.orientationChangesEnabled
81 && (!greeter || !greeter.animating)
83 readonly property bool showingGreeter: greeter && greeter.shown
85 property bool startingUp: true
86 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
88 property int supportedOrientations: {
90 // Ensure we don't rotate during start up
91 return Qt.PrimaryOrientation;
92 } else if (showingGreeter || notifications.topmostIsFullscreen) {
93 return Qt.PrimaryOrientation;
95 return shell.orientations.map(stage.supportedOrientations);
99 readonly property var mainApp: stage.mainApp
103 _onMainAppChanged(mainApp.appId);
107 target: ApplicationManager
109 if (shell.mainApp && shell.mainApp.appId === appId) {
110 _onMainAppChanged(appId);
114 function _onMainAppChanged(appId) {
115 if (wizard.active && appId != "" && appId != "unity8-dash") {
116 // If this happens on first boot, we may be in the
117 // wizard while receiving a call. But a call is more
118 // important than the wizard so just bail out of it.
122 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
123 // If we are in the middle of a call, make dialer lockedApp and show it.
124 // This can happen if user backs out of dialer back to greeter, then
125 // launches dialer again.
126 greeter.lockedApp = appId;
128 greeter.notifyAppFocusRequested(appId);
130 panel.indicators.hide();
131 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
134 // For autopilot consumption
135 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
137 // Note when greeter is waiting on PAM, so that we can disable edges until
138 // we know which user data to show and whether the session is locked.
139 readonly property bool waitingOnGreeter: greeter && greeter.waiting
141 property real edgeSize: units.gu(settings.edgeDragWidth)
144 id: wallpaperResolver
145 objectName: "wallpaperResolver"
147 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
148 readonly property bool hasCustomBackground: background != defaultBackground
150 // Use a cached version of the scaled-down wallpaper (as sometimes the
151 // image can be quite big compared to the device size, including for
152 // our default wallpaper). We use a name=wallpaper argument here to
153 // make sure we don't litter our cache with lots of scaled images. We
154 // only need to bother caching one at a time.
155 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
158 id: backgroundSettings
159 schema.id: "org.gnome.desktop.background"
163 AccountsService.backgroundFile,
164 backgroundSettings.pictureUri,
169 readonly property alias greeter: greeterLoader.item
171 function activateApplication(appId) {
172 // Either open the app in our own session, or -- if we're acting as a
173 // greeter -- ask the user's session to open it for us.
174 if (shell.mode === "greeter") {
175 activateURL("application:///" + appId + ".desktop");
181 function activateURL(url) {
182 SessionBroadcast.requestUrlStart(AccountsService.user, url);
183 greeter.notifyUserRequestedApp();
184 panel.indicators.hide();
187 function startApp(appId) {
188 if (ApplicationManager.findApplication(appId)) {
189 ApplicationManager.requestFocusApplication(appId);
191 print("calling startApplication. active workspace is", WorkspaceManager.activeWorkspace)
192 ApplicationManager.startApplication(appId);
196 function startLockedApp(app) {
197 if (greeter.locked) {
198 greeter.lockedApp = app;
200 startApp(app); // locked apps are always in our same session
204 target: LauncherModel
205 property: "applicationManager"
206 value: ApplicationManager
209 Component.onCompleted: {
210 finishStartUpTimer.start();
219 objectName: "dashCommunicator"
223 id: physicalKeysMapper
224 objectName: "physicalKeysMapper"
226 onPowerKeyLongPressed: dialogs.showPowerDialog();
227 onVolumeDownTriggered: volumeControl.volumeDown();
228 onVolumeUpTriggered: volumeControl.volumeUp();
229 onScreenshotTriggered: itemGrabber.capture(shell);
233 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
238 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
239 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
243 objectName: "windowInputMonitor"
244 onHomeKeyActivated: {
245 // Ignore when greeter is active, to avoid pocket presses
246 if (!greeter.active) {
248 ApplicationManager.requestFocusApplication("unity8-dash");
251 onTouchBegun: { cursor.opacity = 0; }
253 // move the (hidden) cursor to the last known touch position
254 var mappedCoords = mapFromItem(null, pos.x, pos.y);
255 cursor.x = mappedCoords.x;
256 cursor.y = mappedCoords.y;
257 cursor.mouseNeverMoved = false;
263 schema.id: "com.canonical.Unity8"
268 objectName: "panelState"
275 height: parent.height
277 TopLevelWindowModel {
278 id: topLevelSurfaceList
279 objectName: "topLevelSurfaceList"
280 applicationManager: ApplicationManager // it's a singleton
281 surfaceManager: SurfaceManager
282 workspace: screenWindow.screen.currentWorkspace
291 dragAreaWidth: shell.edgeSize
292 background: wallpaperResolver.background
294 applicationManager: ApplicationManager
295 topLevelSurfaceList: topLevelSurfaceList
296 inputMethodRect: inputMethod.visibleRect
297 rightEdgePushProgress: rightEdgeBarrier.progress
299 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
301 : shell.usageScenario
303 mode: usageScenario == "phone" ? "staged"
304 : usageScenario == "tablet" ? "stagedWithSideStage"
307 shellOrientation: shell.orientation
308 shellOrientationAngle: shell.orientationAngle
309 orientations: shell.orientations
310 nativeWidth: shell.nativeWidth
311 nativeHeight: shell.nativeHeight
313 interactive: (!greeter || !greeter.shown)
314 && panel.indicators.fullyClosed
315 && !notifications.useModal
317 onInteractiveChanged: { if (interactive) { focus = true; } }
319 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
320 suspended: greeter.shown
321 altTabPressed: physicalKeysMapper.altTabPressed
322 oskEnabled: shell.oskEnabled
323 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
324 panelState: panelState
326 onSpreadShownChanged: {
327 panel.indicators.hide();
328 panel.applicationMenus.hide();
335 minimumTouchPoints: 4
336 maximumTouchPoints: minimumTouchPoints
338 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
339 touchPoints.length >= minimumTouchPoints &&
340 touchPoints.length <= maximumTouchPoints
341 property bool wasPressed: false
343 onRecognisedPressChanged: {
344 if (recognisedPress) {
350 if (status !== TouchGestureArea.Recognized) {
351 if (status === TouchGestureArea.WaitingForTouch) {
352 if (wasPressed && !dragging) {
353 launcher.openDrawer(true);
364 objectName: "inputMethod"
365 surface: topLevelSurfaceList.inputMethodSurface
368 topMargin: panel.panelHeight
369 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
371 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
376 objectName: "greeterLoader"
378 anchors.topMargin: panel.panelHeight
380 if (shell.mode != "shell") {
381 if (screenWindow.primary) return integratedGreeter;
382 return secondaryGreeter;
384 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
387 item.objectName = "greeter"
389 property bool openDrawerAfterUnlock: false
393 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
394 launcher.openDrawer(false);
395 greeterLoader.openDrawerAfterUnlock = false;
402 id: integratedGreeter
405 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
406 hides: [launcher, panel.indicators, panel.applicationMenus]
407 tabletMode: shell.usageScenario != "phone"
408 forcedUnlock: wizard.active || shell.mode === "full-shell"
409 background: wallpaperResolver.cachedBackground
410 hasCustomBackground: wallpaperResolver.hasCustomBackground
411 allowFingerprint: !dialogs.hasActiveDialog &&
412 !notifications.topmostIsFullscreen &&
413 !panel.indicators.shown
415 // avoid overlapping with Launcher's edge drag area
416 // FIXME: Fix TouchRegistry & friends and remove this workaround
417 // Issue involves launcher's DDA getting disabled on a long
419 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
426 if (!tutorial.running) {
431 onEmergencyCall: startLockedApp("dialer-app")
438 hides: [launcher, panel.indicators]
443 // See powerConnection for why this is useful
444 id: showGreeterDelayed
456 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
457 // We just received an incoming call while locked. The
458 // indicator will have already launched dialer-app for us, but
459 // there is a race between "hasCalls" changing and the dialer
460 // starting up. So in case we lose that race, we'll start/
461 // focus the dialer ourselves here too. Even if the indicator
462 // didn't launch the dialer for some reason (or maybe a call
463 // started via some other means), if an active call is
464 // happening, we want to be in the dialer.
465 startLockedApp("dialer-app")
475 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
476 !callManager.hasCalls && !wizard.active) {
477 // We don't want to simply call greeter.showNow() here, because
478 // that will take too long. Qt will delay button event
479 // handling until the greeter is done loading and may think the
480 // user held down the power button the whole time, leading to a
481 // power dialog being shown. Instead, delay showing the
482 // greeter until we've finished handling the event. We could
483 // make the greeter load asynchronously instead, but that
484 // introduces a whole host of timing issues, especially with
485 // its animations. So this is simpler.
486 showGreeterDelayed.start();
491 function showHome() {
492 greeter.notifyUserRequestedApp();
494 if (shell.mode === "greeter") {
495 SessionBroadcast.requestHomeShown(AccountsService.user);
497 if (!greeter.active) {
498 launcher.openDrawer(false);
500 greeterLoader.openDrawerAfterUnlock = true;
514 anchors.fill: parent //because this draws indicator menus
516 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
517 minimizedPanelHeight: units.gu(3)
518 expandedPanelHeight: units.gu(7)
519 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
520 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
524 available: tutorial.panelEnabled
525 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
526 && (!greeter || !greeter.hasLockedApp)
527 && !shell.waitingOnGreeter
528 && settings.enableIndicatorMenu
530 model: Indicators.IndicatorsModel {
531 // tablet and phone both use the same profile
532 // FIXME: use just "phone" for greeter too, but first fix
533 // greeter app launching to either load the app inside the
534 // greeter or tell the session to load the app. This will
535 // involve taking the url-dispatcher dbus name and using
536 // SessionBroadcast to tell the session.
537 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
538 Component.onCompleted: load();
544 available: (!greeter || !greeter.shown)
545 && !shell.waitingOnGreeter
546 && !stage.spreadShown
549 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
550 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
552 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
553 || greeter.hasLockedApp
554 greeterShown: greeter && greeter.shown
555 hasKeyboard: shell.hasKeyboard
556 panelState: panelState
561 objectName: "launcher"
563 anchors.top: parent.top
564 anchors.topMargin: inverted ? 0 : panel.panelHeight
565 anchors.bottom: parent.bottom
567 dragAreaWidth: shell.edgeSize
568 available: tutorial.launcherEnabled
569 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
570 && !greeter.hasLockedApp
571 && !shell.waitingOnGreeter
572 && settings.enableLauncher
573 inverted: shell.usageScenario !== "desktop"
574 superPressed: physicalKeysMapper.superPressed
575 superTabPressed: physicalKeysMapper.superTabPressed
576 panelWidth: units.gu(settings.launcherWidth)
577 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
578 blurSource: greeter.shown ? greeter : stages
579 topPanelHeight: panel.panelHeight
580 drawerEnabled: !greeter.active
582 onShowDashHome: showHome()
583 onLauncherApplicationSelected: {
584 greeter.notifyUserRequestedApp();
585 shell.activateApplication(appId);
589 panel.indicators.hide()
599 shortcut: Qt.MetaModifier | Qt.Key_A
601 launcher.openDrawer(true);
605 shortcut: Qt.AltModifier | Qt.Key_F1
607 launcher.openForKeyboardNavigation();
611 shortcut: Qt.MetaModifier | Qt.Key_0
613 if (LauncherModel.get(9)) {
614 activateApplication(LauncherModel.get(9).appId);
621 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
623 if (LauncherModel.get(index)) {
624 activateApplication(LauncherModel.get(index).appId);
631 KeyboardShortcutsOverlay {
632 objectName: "shortcutsOverlay"
633 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
634 && height < parent.height - padding - panel.panelHeight
635 anchors.centerIn: parent
636 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
637 anchors.verticalCenterOffset: panel.panelHeight/2
639 opacity: enabled ? 0.95 : 0
641 Behavior on opacity {
642 UbuntuNumberAnimation {}
648 objectName: "tutorial"
651 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
652 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
653 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
654 inputMethod.visible ||
655 (launcher.shown && !launcher.lockedVisible) ||
656 panel.indicators.shown || stage.rightEdgeDragProgress > 0
657 usageScenario: shell.usageScenario
658 lastInputTimestamp: inputFilter.lastInputTimestamp
668 deferred: shell.mode === "greeter"
670 function unlockWhenDoneWithWizard() {
672 Connectivity.unlockAllModems();
676 Component.onCompleted: unlockWhenDoneWithWizard()
677 onActiveChanged: unlockWhenDoneWithWizard()
680 MouseArea { // modal notifications prevent interacting with other contents
682 visible: notifications.useModal
689 model: NotificationBackend.Model
691 hasMouse: shell.hasMouse
692 background: wallpaperResolver.cachedBackground
694 y: topmostIsFullscreen ? 0 : panel.panelHeight
695 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
700 when: overlay.width <= units.gu(60)
702 target: notifications
703 anchors.left: parent.left
704 anchors.right: parent.right
709 when: overlay.width > units.gu(60)
711 target: notifications
712 anchors.left: undefined
713 anchors.right: parent.right
715 PropertyChanges { target: notifications; width: units.gu(38) }
722 enabled: !greeter.shown
724 // NB: it does its own positioning according to the specified edge
728 panel.indicators.hide()
731 material: Component {
737 anchors.centerIn: parent
739 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
740 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
750 objectName: "dialogs"
752 visible: hasActiveDialog
754 usageScenario: shell.usageScenario
755 hasKeyboard: shell.hasKeyboard
757 shutdownFadeOutRectangle.enabled = true;
758 shutdownFadeOutRectangle.visible = true;
759 shutdownFadeOut.start();
764 target: SessionBroadcast
765 onShowHome: if (shell.mode !== "greeter") showHome()
770 objectName: "urlDispatcher"
771 active: shell.mode === "greeter"
772 onUrlRequested: shell.activateURL(url)
779 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
782 ignoreUnknownSignals: true
783 onItemSnapshotRequested: itemGrabber.capture(item)
788 id: cursorHidingTimer
790 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
791 onTriggered: cursor.opacity = 0;
796 objectName: "Pointer-"+screenWindow.objectName
800 topBoundaryOffset: panel.panelHeight
801 enabled: shell.hasMouse && screenWindow.active
804 property bool mouseNeverMoved: true
806 target: cursor; property: "x"; value: shell.width / 2
807 when: cursor.mouseNeverMoved && cursor.visible
810 target: cursor; property: "y"; value: shell.height / 2
811 when: cursor.mouseNeverMoved && cursor.visible
814 confiningItem: stage.itemConfiningMouseCursor
818 readonly property var previewRectangle: stage.previewRectangle.target &&
819 stage.previewRectangle.target.dragging ?
820 stage.previewRectangle : null
822 onPushedLeftBoundary: {
823 if (buttons === Qt.NoButton) {
824 launcher.pushEdge(amount);
825 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
826 previewRectangle.maximizeLeft(amount);
830 onPushedRightBoundary: {
831 if (buttons === Qt.NoButton) {
832 rightEdgeBarrier.push(amount);
833 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
834 previewRectangle.maximizeRight(amount);
838 onPushedTopBoundary: {
839 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
840 previewRectangle.maximize(amount);
843 onPushedTopLeftCorner: {
844 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
845 previewRectangle.maximizeTopLeft(amount);
848 onPushedTopRightCorner: {
849 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
850 previewRectangle.maximizeTopRight(amount);
853 onPushedBottomLeftCorner: {
854 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
855 previewRectangle.maximizeBottomLeft(amount);
858 onPushedBottomRightCorner: {
859 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
860 previewRectangle.maximizeBottomRight(amount);
864 if (previewRectangle) {
865 previewRectangle.stop();
870 mouseNeverMoved = false;
874 Behavior on opacity { UbuntuNumberAnimation {} }
877 // non-visual objects
879 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
884 id: shutdownFadeOutRectangle
891 NumberAnimation on opacity {
896 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
897 DBusUnitySessionService.shutdown();
904 z: shutdownFadeOutRectangle.z + 1
905 active: DebuggingController.logOverlay
907 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