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 applicationsDisplayLoader.item.updateFocusedAppOrientation();
68 function updateFocusedAppOrientationAnimated() {
69 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
71 property bool hasMouse: false
73 // to be read from outside
74 readonly property int mainAppWindowOrientationAngle:
75 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
77 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.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;
92 } else if (applicationsDisplayLoader.item) {
93 return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
96 return Qt.PortraitOrientation
97 | Qt.LandscapeOrientation
98 | Qt.InvertedPortraitOrientation
99 | Qt.InvertedLandscapeOrientation;
103 readonly property var mainApp:
104 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
107 _onMainAppChanged(mainApp.appId);
111 target: ApplicationManager
113 if (shell.mainApp && shell.mainApp.appId === appId) {
114 _onMainAppChanged(appId);
118 function _onMainAppChanged(appId) {
119 if (wizard.active && appId != "" && appId != "unity8-dash") {
120 // If this happens on first boot, we may be in the
121 // wizard while receiving a call. But a call is more
122 // important than the wizard so just bail out of it.
126 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
127 // If we are in the middle of a call, make dialer lockedApp and show it.
128 // This can happen if user backs out of dialer back to greeter, then
129 // launches dialer again.
130 greeter.lockedApp = appId;
132 greeter.notifyAppFocusRequested(appId);
134 panel.indicators.hide();
138 // For autopilot consumption
139 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
141 // Note when greeter is waiting on PAM, so that we can disable edges until
142 // we know which user data to show and whether the session is locked.
143 readonly property bool waitingOnGreeter: greeter && greeter.waiting
145 property real edgeSize: units.gu(settings.edgeDragWidth)
148 id: wallpaperResolver
149 objectName: "wallpaperResolver"
151 readonly property url defaultBackground: "file:///usr/share/backgrounds/warty-final-ubuntu.png"
152 readonly property bool hasCustomBackground: background != defaultBackground
154 // Use a cached version of the scaled-down wallpaper (as sometimes the
155 // image can be quite big compared to the device size, including for
156 // our default wallpaper). We use a name=wallpaper argument here to
157 // make sure we don't litter our cache with lots of scaled images. We
158 // only need to bother caching one at a time.
159 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
162 id: backgroundSettings
163 schema.id: "org.gnome.desktop.background"
167 AccountsService.backgroundFile,
168 backgroundSettings.pictureUri,
173 readonly property alias greeter: greeterLoader.item
175 function activateApplication(appId) {
176 // Either open the app in our own session, or -- if we're acting as a
177 // greeter -- ask the user's session to open it for us.
178 if (shell.mode === "greeter") {
179 activateURL("application:///" + appId + ".desktop");
185 function activateURL(url) {
186 SessionBroadcast.requestUrlStart(AccountsService.user, url);
187 greeter.notifyUserRequestedApp();
188 panel.indicators.hide();
191 function startApp(appId) {
192 if (ApplicationManager.findApplication(appId)) {
193 ApplicationManager.requestFocusApplication(appId);
195 ApplicationManager.startApplication(appId);
199 function startLockedApp(app) {
200 if (greeter.locked) {
201 greeter.lockedApp = app;
203 startApp(app); // locked apps are always in our same session
207 target: LauncherModel
208 property: "applicationManager"
209 value: ApplicationManager
212 Component.onCompleted: {
213 finishStartUpTimer.start();
222 objectName: "dashCommunicator"
226 id: physicalKeysMapper
227 objectName: "physicalKeysMapper"
229 onPowerKeyLongPressed: dialogs.showPowerDialog();
230 onVolumeDownTriggered: volumeControl.volumeDown();
231 onVolumeUpTriggered: volumeControl.volumeUp();
232 onScreenshotTriggered: itemGrabber.capture(shell);
236 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
241 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
242 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
246 objectName: "windowInputMonitor"
247 onHomeKeyActivated: {
248 // Ignore when greeter is active, to avoid pocket presses
249 if (!greeter.active) {
254 onTouchBegun: { cursor.opacity = 0; }
256 // move the (hidden) cursor to the last known touch position
257 var mappedCoords = mapFromItem(null, pos.x, pos.y);
258 cursor.x = mappedCoords.x;
259 cursor.y = mappedCoords.y;
260 cursor.mouseNeverMoved = false;
266 schema.id: "com.canonical.Unity8"
273 height: parent.height
275 TopLevelSurfaceList {
276 id: topLevelSurfaceList
277 objectName: "topLevelSurfaceList"
278 applicationsModel: ApplicationManager
282 id: applicationsDisplayLoader
283 objectName: "applicationsDisplayLoader"
286 // When we have a locked app, we only want to show that one app.
287 // FIXME: do this in a less traumatic way. We currently only allow
288 // locked apps in phone mode (see FIXME in Lockscreen component in
289 // this same file). When that changes, we need to do something
290 // nicer here. But this code is currently just to prevent a
291 // theoretical attack where user enters lockedApp mode, then makes
292 // the screen larger (maybe connects to monitor) and tries to enter
295 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
297 : shell.usageScenario
298 readonly property string qmlComponent: {
299 if (applicationsDisplayLoader.usageScenario === "phone") {
300 return "Stages/PhoneStage.qml";
301 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
302 return "Stages/TabletStage.qml";
304 return "Stages/DesktopStage.qml";
307 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
308 // Currently the new one will get loaded while the old is still hanging
309 // around for a bit, which might lead to conflicts where both stages
310 // change the model simultaneously.
311 onQmlComponentChanged: {
312 if (item) item.stageAboutToBeUnloaded();
313 source = qmlComponent;
316 property bool interactive: (!greeter || !greeter.shown)
317 && panel.indicators.fullyClosed
318 && launcher.progress == 0
319 && !notifications.useModal
321 onInteractiveChanged: { if (interactive) { focus = true; } }
324 target: applicationsDisplayLoader.item
329 target: applicationsDisplayLoader.item
330 property: "objectName"
334 target: applicationsDisplayLoader.item
335 property: "dragAreaWidth"
336 value: shell.edgeSize
339 target: applicationsDisplayLoader.item
340 property: "maximizedAppTopMargin"
341 // Not just using panel.panelHeight as that changes depending on the focused app.
342 value: panel.indicators.minimizedPanelHeight
345 target: applicationsDisplayLoader.item
346 property: "interactive"
347 value: applicationsDisplayLoader.interactive
350 target: applicationsDisplayLoader.item
351 property: "spreadEnabled"
352 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
355 target: applicationsDisplayLoader.item
356 property: "inverseProgress"
357 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
360 target: applicationsDisplayLoader.item
361 property: "shellOrientationAngle"
362 value: shell.orientationAngle
365 target: applicationsDisplayLoader.item
366 property: "shellOrientation"
367 value: shell.orientation
370 target: applicationsDisplayLoader.item
371 property: "orientations"
372 value: shell.orientations
375 target: applicationsDisplayLoader.item
376 property: "background"
377 value: wallpaperResolver.cachedBackground
380 target: applicationsDisplayLoader.item
381 property: "nativeWidth"
382 value: shell.nativeWidth
385 target: applicationsDisplayLoader.item
386 property: "nativeHeight"
387 value: shell.nativeHeight
390 target: applicationsDisplayLoader.item
391 property: "beingResized"
392 value: shell.beingResized
395 target: applicationsDisplayLoader.item
396 property: "keepDashRunning"
397 value: launcher.shown || launcher.dashSwipe
400 target: applicationsDisplayLoader.item
401 property: "suspended"
405 target: applicationsDisplayLoader.item
406 property: "altTabPressed"
407 value: physicalKeysMapper.altTabPressed
410 target: applicationsDisplayLoader.item
411 property: "leftMargin"
412 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
415 target: applicationsDisplayLoader.item
416 property: "applicationManager"
417 value: ApplicationManager
420 target: applicationsDisplayLoader.item
421 property: "topLevelSurfaceList"
422 value: topLevelSurfaceList
425 target: applicationsDisplayLoader.item
426 property: "oskEnabled"
427 value: shell.oskEnabled
434 objectName: "inputMethod"
437 topMargin: panel.panelHeight
438 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
440 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
446 anchors.topMargin: panel.panelHeight
447 sourceComponent: shell.mode != "shell" ? integratedGreeter :
448 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
450 item.objectName = "greeter"
455 id: integratedGreeter
458 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
459 hides: [launcher, panel.indicators, panel.applicationMenus]
460 tabletMode: shell.usageScenario != "phone"
461 launcherOffset: launcher.progress
462 forcedUnlock: wizard.active || shell.mode === "full-shell"
463 background: wallpaperResolver.cachedBackground
464 hasCustomBackground: wallpaperResolver.hasCustomBackground
465 allowFingerprint: !dialogs.hasActiveDialog &&
466 !notifications.topmostIsFullscreen &&
467 !panel.indicators.shown
469 // avoid overlapping with Launcher's edge drag area
470 // FIXME: Fix TouchRegistry & friends and remove this workaround
471 // Issue involves launcher's DDA getting disabled on a long
473 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
480 if (!tutorial.running) {
485 onEmergencyCall: startLockedApp("dialer-app")
490 // See powerConnection for why this is useful
491 id: showGreeterDelayed
503 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
504 // We just received an incoming call while locked. The
505 // indicator will have already launched dialer-app for us, but
506 // there is a race between "hasCalls" changing and the dialer
507 // starting up. So in case we lose that race, we'll start/
508 // focus the dialer ourselves here too. Even if the indicator
509 // didn't launch the dialer for some reason (or maybe a call
510 // started via some other means), if an active call is
511 // happening, we want to be in the dialer.
512 startLockedApp("dialer-app")
522 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
523 !callManager.hasCalls && !wizard.active) {
524 // We don't want to simply call greeter.showNow() here, because
525 // that will take too long. Qt will delay button event
526 // handling until the greeter is done loading and may think the
527 // user held down the power button the whole time, leading to a
528 // power dialog being shown. Instead, delay showing the
529 // greeter until we've finished handling the event. We could
530 // make the greeter load asynchronously instead, but that
531 // introduces a whole host of timing issues, especially with
532 // its animations. So this is simpler.
533 showGreeterDelayed.start();
538 function showHome() {
539 greeter.notifyUserRequestedApp();
541 if (shell.mode === "greeter") {
542 SessionBroadcast.requestHomeShown(AccountsService.user);
544 var animate = !LightDMService.greeter.active && !stages.shown;
545 dash.setCurrentScope(0, animate, false);
546 ApplicationManager.requestFocusApplication("unity8-dash");
550 function showDash() {
551 if (greeter.notifyShowingDashFromDrag()) {
555 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
556 && ApplicationManager.focusedApplicationId != "unity8-dash") {
557 ApplicationManager.requestFocusApplication("unity8-dash")
571 anchors.fill: parent //because this draws indicator menus
573 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
574 minimizedPanelHeight: units.gu(3)
575 expandedPanelHeight: units.gu(7)
576 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
577 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
581 available: tutorial.panelEnabled
582 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
583 && (!greeter || !greeter.hasLockedApp)
584 && !shell.waitingOnGreeter
586 model: Indicators.IndicatorsModel {
587 // tablet and phone both use the same profile
588 // FIXME: use just "phone" for greeter too, but first fix
589 // greeter app launching to either load the app inside the
590 // greeter or tell the session to load the app. This will
591 // involve taking the url-dispatcher dbus name and using
592 // SessionBroadcast to tell the session.
593 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
594 Component.onCompleted: load();
602 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
603 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
605 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
606 || greeter.hasLockedApp
607 locked: greeter && greeter.active
608 greeterShown: greeter && greeter.shown
613 objectName: "launcher"
616 * Since the Dash doesn't have the same controll over surfaces that the
617 * Shell does, it can't slowly move the scope out of the way, as the shell
618 * does with apps, and the dash is show instantly. This allows for some
619 * leeway and prevents accidental home swipes.
621 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
622 readonly property bool dashSwipe: progress > offset
624 anchors.top: parent.top
625 anchors.topMargin: inverted ? 0 : panel.panelHeight
626 anchors.bottom: parent.bottom
628 dragAreaWidth: shell.edgeSize
629 available: tutorial.launcherEnabled
630 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
631 && !greeter.hasLockedApp
632 && !shell.waitingOnGreeter
633 inverted: shell.usageScenario !== "desktop"
634 superPressed: physicalKeysMapper.superPressed
635 superTabPressed: physicalKeysMapper.superTabPressed
636 panelWidth: units.gu(settings.launcherWidth)
637 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
639 onShowDashHome: showHome()
641 onDashSwipeChanged: {
643 dash.setCurrentScope(0, false, true)
646 onLauncherApplicationSelected: {
647 greeter.notifyUserRequestedApp();
648 shell.activateApplication(appId);
652 panel.indicators.hide()
657 applicationsDisplayLoader.focus = true;
662 shortcut: Qt.AltModifier | Qt.Key_F1
664 launcher.openForKeyboardNavigation();
668 shortcut: Qt.MetaModifier | Qt.Key_0
670 if (LauncherModel.get(9)) {
671 activateApplication(LauncherModel.get(9).appId);
678 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
680 if (LauncherModel.get(index)) {
681 activateApplication(LauncherModel.get(index).appId);
688 KeyboardShortcutsOverlay {
689 objectName: "shortcutsOverlay"
690 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
691 && height < parent.height - padding - panel.panelHeight
692 anchors.centerIn: parent
693 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
694 anchors.verticalCenterOffset: panel.panelHeight/2
696 opacity: enabled ? 0.95 : 0
698 Behavior on opacity {
699 UbuntuNumberAnimation {}
705 objectName: "tutorial"
708 paused: callManager.hasCalls || !greeter || greeter.active ||
710 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
711 inputMethod.visible ||
712 (launcher.shown && !launcher.lockedVisible) ||
713 panel.indicators.shown || stage.dragProgress > 0
714 usageScenario: shell.usageScenario
715 lastInputTimestamp: inputFilter.lastInputTimestamp
718 stage: applicationsDisplayLoader.item
725 deferred: shell.mode === "greeter"
727 function unlockWhenDoneWithWizard() {
729 Connectivity.unlockAllModems();
733 Component.onCompleted: unlockWhenDoneWithWizard()
734 onActiveChanged: unlockWhenDoneWithWizard()
737 MouseArea { // modal notifications prevent interacting with other contents
739 visible: notifications.useModal
746 model: NotificationBackend.Model
748 hasMouse: shell.hasMouse
749 background: wallpaperResolver.cachedBackground
751 y: topmostIsFullscreen ? 0 : panel.panelHeight
752 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
757 when: overlay.width <= units.gu(60)
759 target: notifications
760 anchors.left: parent.left
761 anchors.right: parent.right
766 when: overlay.width > units.gu(60)
768 target: notifications
769 anchors.left: undefined
770 anchors.right: parent.right
772 PropertyChanges { target: notifications; width: units.gu(38) }
780 objectName: "dialogs"
783 usageScenario: shell.usageScenario
785 shutdownFadeOutRectangle.enabled = true;
786 shutdownFadeOutRectangle.visible = true;
787 shutdownFadeOut.start();
792 target: SessionBroadcast
793 onShowHome: if (shell.mode !== "greeter") showHome()
798 objectName: "urlDispatcher"
799 active: shell.mode === "greeter"
800 onUrlRequested: shell.activateURL(url)
807 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
809 target: applicationsDisplayLoader.item
810 ignoreUnknownSignals: true
811 onItemSnapshotRequested: itemGrabber.capture(item)
817 visible: shell.hasMouse
819 topBoundaryOffset: panel.panelHeight
821 confiningItem: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.itemConfiningMouseCursor : null
823 property bool mouseNeverMoved: true
825 target: cursor; property: "x"; value: shell.width / 2
826 when: cursor.mouseNeverMoved && cursor.visible
829 target: cursor; property: "y"; value: shell.height / 2
830 when: cursor.mouseNeverMoved && cursor.visible
835 readonly property var previewRectangle: applicationsDisplayLoader.item && applicationsDisplayLoader.item.previewRectangle &&
836 applicationsDisplayLoader.item.previewRectangle.target &&
837 applicationsDisplayLoader.item.previewRectangle.target.dragging ?
838 applicationsDisplayLoader.item.previewRectangle : null
840 onPushedLeftBoundary: {
841 if (buttons === Qt.NoButton) {
842 launcher.pushEdge(amount);
843 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
844 previewRectangle.maximizeLeft(amount);
848 onPushedRightBoundary: {
849 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
850 && applicationsDisplayLoader.item.pushRightEdge) {
851 applicationsDisplayLoader.item.pushRightEdge(amount);
852 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
853 previewRectangle.maximizeRight(amount);
857 onPushedTopBoundary: {
858 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
859 previewRectangle.maximize(amount);
862 onPushedTopLeftCorner: {
863 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
864 previewRectangle.maximizeTopLeft(amount);
867 onPushedTopRightCorner: {
868 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
869 previewRectangle.maximizeTopRight(amount);
872 onPushedBottomLeftCorner: {
873 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
874 previewRectangle.maximizeBottomLeft(amount);
877 onPushedBottomRightCorner: {
878 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
879 previewRectangle.maximizeBottomRight(amount);
883 if (previewRectangle) {
884 previewRectangle.stop();
889 mouseNeverMoved = false;
898 id: shutdownFadeOutRectangle
905 NumberAnimation on opacity {
910 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
911 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