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();
218 indicators: panel.indicators
223 objectName: "dashCommunicator"
227 id: physicalKeysMapper
228 objectName: "physicalKeysMapper"
230 onPowerKeyLongPressed: dialogs.showPowerDialog();
231 onVolumeDownTriggered: volumeControl.volumeDown();
232 onVolumeUpTriggered: volumeControl.volumeUp();
233 onScreenshotTriggered: itemGrabber.capture(shell);
237 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
242 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
243 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
247 objectName: "windowInputMonitor"
248 onHomeKeyActivated: {
249 // Ignore when greeter is active, to avoid pocket presses
250 if (!greeter.active) {
255 onTouchBegun: { cursor.opacity = 0; }
257 // move the (hidden) cursor to the last known touch position
258 var mappedCoords = mapFromItem(null, pos.x, pos.y);
259 cursor.x = mappedCoords.x;
260 cursor.y = mappedCoords.y;
261 cursor.mouseNeverMoved = false;
267 schema.id: "com.canonical.Unity8"
274 height: parent.height
276 TopLevelSurfaceList {
277 id: topLevelSurfaceList
278 objectName: "topLevelSurfaceList"
279 applicationsModel: ApplicationManager
283 id: applicationsDisplayLoader
284 objectName: "applicationsDisplayLoader"
287 // When we have a locked app, we only want to show that one app.
288 // FIXME: do this in a less traumatic way. We currently only allow
289 // locked apps in phone mode (see FIXME in Lockscreen component in
290 // this same file). When that changes, we need to do something
291 // nicer here. But this code is currently just to prevent a
292 // theoretical attack where user enters lockedApp mode, then makes
293 // the screen larger (maybe connects to monitor) and tries to enter
296 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
298 : shell.usageScenario
299 readonly property string qmlComponent: {
300 if (applicationsDisplayLoader.usageScenario === "phone") {
301 return "Stages/PhoneStage.qml";
302 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
303 return "Stages/TabletStage.qml";
305 return "Stages/DesktopStage.qml";
308 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
309 // Currently the new one will get loaded while the old is still hanging
310 // around for a bit, which might lead to conflicts where both stages
311 // change the model simultaneously.
312 onQmlComponentChanged: {
313 if (item) item.stageAboutToBeUnloaded();
314 source = qmlComponent;
317 property bool interactive: (!greeter || !greeter.shown)
318 && panel.indicators.fullyClosed
319 && launcher.progress == 0
320 && !notifications.useModal
322 onInteractiveChanged: { if (interactive) { focus = true; } }
325 target: applicationsDisplayLoader.item
330 target: applicationsDisplayLoader.item
331 property: "objectName"
335 target: applicationsDisplayLoader.item
336 property: "dragAreaWidth"
337 value: shell.edgeSize
340 target: applicationsDisplayLoader.item
341 property: "maximizedAppTopMargin"
342 // Not just using panel.panelHeight as that changes depending on the focused app.
343 value: panel.indicators.minimizedPanelHeight
346 target: applicationsDisplayLoader.item
347 property: "interactive"
348 value: applicationsDisplayLoader.interactive
351 target: applicationsDisplayLoader.item
352 property: "spreadEnabled"
353 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
356 target: applicationsDisplayLoader.item
357 property: "inverseProgress"
358 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
361 target: applicationsDisplayLoader.item
362 property: "shellOrientationAngle"
363 value: shell.orientationAngle
366 target: applicationsDisplayLoader.item
367 property: "shellOrientation"
368 value: shell.orientation
371 target: applicationsDisplayLoader.item
372 property: "orientations"
373 value: shell.orientations
376 target: applicationsDisplayLoader.item
377 property: "background"
378 value: wallpaperResolver.cachedBackground
381 target: applicationsDisplayLoader.item
382 property: "nativeWidth"
383 value: shell.nativeWidth
386 target: applicationsDisplayLoader.item
387 property: "nativeHeight"
388 value: shell.nativeHeight
391 target: applicationsDisplayLoader.item
392 property: "beingResized"
393 value: shell.beingResized
396 target: applicationsDisplayLoader.item
397 property: "keepDashRunning"
398 value: launcher.shown || launcher.dashSwipe
401 target: applicationsDisplayLoader.item
402 property: "suspended"
406 target: applicationsDisplayLoader.item
407 property: "altTabPressed"
408 value: physicalKeysMapper.altTabPressed
411 target: applicationsDisplayLoader.item
412 property: "leftMargin"
413 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
416 target: applicationsDisplayLoader.item
417 property: "applicationManager"
418 value: ApplicationManager
421 target: applicationsDisplayLoader.item
422 property: "topLevelSurfaceList"
423 value: topLevelSurfaceList
426 target: applicationsDisplayLoader.item
427 property: "oskEnabled"
428 value: shell.oskEnabled
435 objectName: "inputMethod"
438 topMargin: panel.panelHeight
439 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
441 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
447 anchors.topMargin: panel.panelHeight
448 sourceComponent: shell.mode != "shell" ? integratedGreeter :
449 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
451 item.objectName = "greeter"
456 id: integratedGreeter
459 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
460 hides: [launcher, panel.indicators]
461 tabletMode: shell.usageScenario != "phone"
462 launcherOffset: launcher.progress
463 forcedUnlock: wizard.active || shell.mode === "full-shell"
464 background: wallpaperResolver.cachedBackground
465 hasCustomBackground: wallpaperResolver.hasCustomBackground
466 allowFingerprint: !dialogs.hasActiveDialog &&
467 !notifications.topmostIsFullscreen &&
468 !panel.indicators.shown
470 // avoid overlapping with Launcher's edge drag area
471 // FIXME: Fix TouchRegistry & friends and remove this workaround
472 // Issue involves launcher's DDA getting disabled on a long
474 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
481 if (!tutorial.running) {
486 onEmergencyCall: startLockedApp("dialer-app")
491 // See powerConnection for why this is useful
492 id: showGreeterDelayed
504 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
505 // We just received an incoming call while locked. The
506 // indicator will have already launched dialer-app for us, but
507 // there is a race between "hasCalls" changing and the dialer
508 // starting up. So in case we lose that race, we'll start/
509 // focus the dialer ourselves here too. Even if the indicator
510 // didn't launch the dialer for some reason (or maybe a call
511 // started via some other means), if an active call is
512 // happening, we want to be in the dialer.
513 startLockedApp("dialer-app")
523 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
524 !callManager.hasCalls && !wizard.active) {
525 // We don't want to simply call greeter.showNow() here, because
526 // that will take too long. Qt will delay button event
527 // handling until the greeter is done loading and may think the
528 // user held down the power button the whole time, leading to a
529 // power dialog being shown. Instead, delay showing the
530 // greeter until we've finished handling the event. We could
531 // make the greeter load asynchronously instead, but that
532 // introduces a whole host of timing issues, especially with
533 // its animations. So this is simpler.
534 showGreeterDelayed.start();
539 function showHome() {
540 greeter.notifyUserRequestedApp();
542 if (shell.mode === "greeter") {
543 SessionBroadcast.requestHomeShown(AccountsService.user);
545 var animate = !LightDMService.greeter.active && !stages.shown;
546 dash.setCurrentScope(0, animate, false);
547 ApplicationManager.requestFocusApplication("unity8-dash");
551 function showDash() {
552 if (greeter.notifyShowingDashFromDrag()) {
556 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
557 && ApplicationManager.focusedApplicationId != "unity8-dash") {
558 ApplicationManager.requestFocusApplication("unity8-dash")
572 anchors.fill: parent //because this draws indicator menus
575 available: tutorial.panelEnabled
576 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
577 && (!greeter || !greeter.hasLockedApp)
578 && !shell.waitingOnGreeter
579 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
581 minimizedPanelHeight: units.gu(3)
582 expandedPanelHeight: units.gu(7)
584 indicatorsModel: Indicators.IndicatorsModel {
585 // tablet and phone both use the same profile
586 // FIXME: use just "phone" for greeter too, but first fix
587 // greeter app launching to either load the app inside the
588 // greeter or tell the session to load the app. This will
589 // involve taking the url-dispatcher dbus name and using
590 // SessionBroadcast to tell the session.
591 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
592 Component.onCompleted: load();
597 greeterShown: greeter.shown
600 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
601 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
603 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
604 || greeter.hasLockedApp
605 locked: greeter && greeter.active
610 objectName: "launcher"
613 * Since the Dash doesn't have the same controll over surfaces that the
614 * Shell does, it can't slowly move the scope out of the way, as the shell
615 * does with apps, and the dash is show instantly. This allows for some
616 * leeway and prevents accidental home swipes.
618 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
619 readonly property bool dashSwipe: progress > offset
621 anchors.top: parent.top
622 anchors.topMargin: inverted ? 0 : panel.panelHeight
623 anchors.bottom: parent.bottom
625 dragAreaWidth: shell.edgeSize
626 available: tutorial.launcherEnabled
627 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
628 && !greeter.hasLockedApp
629 && !shell.waitingOnGreeter
630 inverted: shell.usageScenario !== "desktop"
631 superPressed: physicalKeysMapper.superPressed
632 superTabPressed: physicalKeysMapper.superTabPressed
633 panelWidth: units.gu(settings.launcherWidth)
634 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
636 onShowDashHome: showHome()
638 onDashSwipeChanged: {
640 dash.setCurrentScope(0, false, true)
643 onLauncherApplicationSelected: {
644 greeter.notifyUserRequestedApp();
645 shell.activateApplication(appId);
649 panel.indicators.hide()
654 applicationsDisplayLoader.focus = true;
659 shortcut: Qt.AltModifier | Qt.Key_F1
661 launcher.openForKeyboardNavigation();
665 shortcut: Qt.MetaModifier | Qt.Key_0
667 if (LauncherModel.get(9)) {
668 activateApplication(LauncherModel.get(9).appId);
675 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
677 if (LauncherModel.get(index)) {
678 activateApplication(LauncherModel.get(index).appId);
685 KeyboardShortcutsOverlay {
686 objectName: "shortcutsOverlay"
687 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
688 && height < parent.height - padding - panel.panelHeight
689 anchors.centerIn: parent
690 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
691 anchors.verticalCenterOffset: panel.panelHeight/2
693 opacity: enabled ? 0.95 : 0
695 Behavior on opacity {
696 UbuntuNumberAnimation {}
702 objectName: "tutorial"
705 paused: callManager.hasCalls || !greeter || greeter.active ||
707 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
708 inputMethod.visible ||
709 (launcher.shown && !launcher.lockedVisible) ||
710 panel.indicators.shown || stage.dragProgress > 0
711 usageScenario: shell.usageScenario
712 lastInputTimestamp: inputFilter.lastInputTimestamp
715 stage: applicationsDisplayLoader.item
722 deferred: shell.mode === "greeter"
724 function unlockWhenDoneWithWizard() {
726 Connectivity.unlockAllModems();
730 Component.onCompleted: unlockWhenDoneWithWizard()
731 onActiveChanged: unlockWhenDoneWithWizard()
734 MouseArea { // modal notifications prevent interacting with other contents
736 visible: notifications.useModal
743 model: NotificationBackend.Model
745 hasMouse: shell.hasMouse
746 background: wallpaperResolver.cachedBackground
748 y: topmostIsFullscreen ? 0 : panel.panelHeight
749 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
754 when: overlay.width <= units.gu(60)
756 target: notifications
757 anchors.left: parent.left
758 anchors.right: parent.right
763 when: overlay.width > units.gu(60)
765 target: notifications
766 anchors.left: undefined
767 anchors.right: parent.right
769 PropertyChanges { target: notifications; width: units.gu(38) }
777 objectName: "dialogs"
780 usageScenario: shell.usageScenario
782 shutdownFadeOutRectangle.enabled = true;
783 shutdownFadeOutRectangle.visible = true;
784 shutdownFadeOut.start();
789 target: SessionBroadcast
790 onShowHome: if (shell.mode !== "greeter") showHome()
795 objectName: "urlDispatcher"
796 active: shell.mode === "greeter"
797 onUrlRequested: shell.activateURL(url)
804 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
806 target: applicationsDisplayLoader.item
807 ignoreUnknownSignals: true
808 onItemSnapshotRequested: itemGrabber.capture(item)
814 visible: shell.hasMouse
817 property bool mouseNeverMoved: true
819 target: cursor; property: "x"; value: shell.width / 2
820 when: cursor.mouseNeverMoved && cursor.visible
823 target: cursor; property: "y"; value: shell.height / 2
824 when: cursor.mouseNeverMoved && cursor.visible
829 onPushedLeftBoundary: {
830 if (buttons === Qt.NoButton) {
831 launcher.pushEdge(amount);
835 onPushedRightBoundary: {
836 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
837 && applicationsDisplayLoader.item.pushRightEdge) {
838 applicationsDisplayLoader.item.pushRightEdge(amount);
843 mouseNeverMoved = false;
852 id: shutdownFadeOutRectangle
859 NumberAnimation on opacity {
864 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
865 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