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
30import LightDM 0.1 as LightDM
33import SessionBroadcast 0.1
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 0.1
53 theme.name: "Ubuntu.Components.Themes.SuruDark"
55 // to be set from outside
56 property int orientationAngle: 0
57 property int orientation
58 property Orientations orientations
59 property real nativeWidth
60 property real nativeHeight
61 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
62 property bool beingResized
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 applicationsDisplayLoader.item.updateFocusedAppOrientation();
69 function updateFocusedAppOrientationAnimated() {
70 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
72 property bool hasMouse: false
74 // to be read from outside
75 readonly property int mainAppWindowOrientationAngle:
76 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
78 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
79 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.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;
93 } else if (applicationsDisplayLoader.item) {
94 return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
97 return Qt.PortraitOrientation
98 | Qt.LandscapeOrientation
99 | Qt.InvertedPortraitOrientation
100 | Qt.InvertedLandscapeOrientation;
104 readonly property var mainApp:
105 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
108 _onMainAppChanged(mainApp.appId);
112 target: ApplicationManager
114 if (shell.mainApp && shell.mainApp.appId === appId) {
115 _onMainAppChanged(appId);
119 function _onMainAppChanged(appId) {
120 if (wizard.active && appId != "" && appId != "unity8-dash") {
121 // If this happens on first boot, we may be in the
122 // wizard while receiving a call. But a call is more
123 // important than the wizard so just bail out of it.
127 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
128 // If we are in the middle of a call, make dialer lockedApp and show it.
129 // This can happen if user backs out of dialer back to greeter, then
130 // launches dialer again.
131 greeter.lockedApp = appId;
133 greeter.notifyAppFocusRequested(appId);
135 panel.indicators.hide();
139 // For autopilot consumption
140 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
142 // Note when greeter is waiting on PAM, so that we can disable edges until
143 // we know which user data to show and whether the session is locked.
144 readonly property bool waitingOnGreeter: greeter && greeter.waiting
146 property real edgeSize: units.gu(settings.edgeDragWidth)
149 id: wallpaperResolver
150 objectName: "wallpaperResolver"
152 readonly property url defaultBackground: "file:///usr/share/backgrounds/warty-final-ubuntu.png"
153 readonly property bool hasCustomBackground: background != defaultBackground
155 // Use a cached version of the scaled-down wallpaper (as sometimes the
156 // image can be quite big compared to the device size, including for
157 // our default wallpaper). We use a name=wallpaper argument here to
158 // make sure we don't litter our cache with lots of scaled images. We
159 // only need to bother caching one at a time.
160 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
163 id: backgroundSettings
164 schema.id: "org.gnome.desktop.background"
168 AccountsService.backgroundFile,
169 backgroundSettings.pictureUri,
174 readonly property alias greeter: greeterLoader.item
176 function activateApplication(appId) {
177 // Either open the app in our own session, or -- if we're acting as a
178 // greeter -- ask the user's session to open it for us.
179 if (shell.mode === "greeter") {
180 activateURL("application:///" + appId + ".desktop");
186 function activateURL(url) {
187 SessionBroadcast.requestUrlStart(AccountsService.user, url);
188 greeter.notifyUserRequestedApp();
189 panel.indicators.hide();
192 function startApp(appId) {
193 if (ApplicationManager.findApplication(appId)) {
194 ApplicationManager.requestFocusApplication(appId);
196 ApplicationManager.startApplication(appId);
200 function startLockedApp(app) {
201 if (greeter.locked) {
202 greeter.lockedApp = app;
204 startApp(app); // locked apps are always in our same session
208 target: LauncherModel
209 property: "applicationManager"
210 value: ApplicationManager
214 target: AccountsService
215 property: "greeterMode"
216 value: shell.mode === "greeter"
219 Component.onCompleted: {
220 finishStartUpTimer.start();
225 indicators: panel.indicators
230 objectName: "dashCommunicator"
234 id: physicalKeysMapper
235 objectName: "physicalKeysMapper"
237 onPowerKeyLongPressed: dialogs.showPowerDialog();
238 onVolumeDownTriggered: volumeControl.volumeDown();
239 onVolumeUpTriggered: volumeControl.volumeUp();
240 onScreenshotTriggered: itemGrabber.capture(shell);
244 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
249 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
250 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
254 objectName: "windowInputMonitor"
255 onHomeKeyActivated: {
256 // Ignore when greeter is active, to avoid pocket presses
257 if (!greeter.active) {
262 onTouchBegun: { cursor.opacity = 0; }
264 // move the (hidden) cursor to the last known touch position
265 var mappedCoords = mapFromItem(null, pos.x, pos.y);
266 cursor.x = mappedCoords.x;
267 cursor.y = mappedCoords.y;
268 cursor.mouseNeverMoved = false;
274 schema.id: "com.canonical.Unity8"
281 height: parent.height
283 TopLevelSurfaceList {
284 id: topLevelSurfaceList
285 objectName: "topLevelSurfaceList"
286 applicationsModel: ApplicationManager
290 id: applicationsDisplayLoader
291 objectName: "applicationsDisplayLoader"
294 // When we have a locked app, we only want to show that one app.
295 // FIXME: do this in a less traumatic way. We currently only allow
296 // locked apps in phone mode (see FIXME in Lockscreen component in
297 // this same file). When that changes, we need to do something
298 // nicer here. But this code is currently just to prevent a
299 // theoretical attack where user enters lockedApp mode, then makes
300 // the screen larger (maybe connects to monitor) and tries to enter
303 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
305 : shell.usageScenario
306 readonly property string qmlComponent: {
307 if (applicationsDisplayLoader.usageScenario === "phone") {
308 return "Stages/PhoneStage.qml";
309 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
310 return "Stages/TabletStage.qml";
312 return "Stages/DesktopStage.qml";
315 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
316 // Currently the new one will get loaded while the old is still hanging
317 // around for a bit, which might lead to conflicts where both stages
318 // change the model simultaneously.
319 onQmlComponentChanged: {
320 if (item) item.stageAboutToBeUnloaded();
321 source = qmlComponent;
324 property bool interactive: (!greeter || !greeter.shown)
325 && panel.indicators.fullyClosed
326 && launcher.progress == 0
327 && !notifications.useModal
329 onInteractiveChanged: { if (interactive) { focus = true; } }
332 target: applicationsDisplayLoader.item
337 target: applicationsDisplayLoader.item
338 property: "objectName"
342 target: applicationsDisplayLoader.item
343 property: "dragAreaWidth"
344 value: shell.edgeSize
347 target: applicationsDisplayLoader.item
348 property: "maximizedAppTopMargin"
349 // Not just using panel.panelHeight as that changes depending on the focused app.
350 value: panel.indicators.minimizedPanelHeight
353 target: applicationsDisplayLoader.item
354 property: "interactive"
355 value: applicationsDisplayLoader.interactive
358 target: applicationsDisplayLoader.item
359 property: "spreadEnabled"
360 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
363 target: applicationsDisplayLoader.item
364 property: "inverseProgress"
365 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
368 target: applicationsDisplayLoader.item
369 property: "shellOrientationAngle"
370 value: shell.orientationAngle
373 target: applicationsDisplayLoader.item
374 property: "shellOrientation"
375 value: shell.orientation
378 target: applicationsDisplayLoader.item
379 property: "orientations"
380 value: shell.orientations
383 target: applicationsDisplayLoader.item
384 property: "background"
385 value: wallpaperResolver.cachedBackground
388 target: applicationsDisplayLoader.item
389 property: "nativeWidth"
390 value: shell.nativeWidth
393 target: applicationsDisplayLoader.item
394 property: "nativeHeight"
395 value: shell.nativeHeight
398 target: applicationsDisplayLoader.item
399 property: "beingResized"
400 value: shell.beingResized
403 target: applicationsDisplayLoader.item
404 property: "keepDashRunning"
405 value: launcher.shown || launcher.dashSwipe
408 target: applicationsDisplayLoader.item
409 property: "suspended"
413 target: applicationsDisplayLoader.item
414 property: "altTabPressed"
415 value: physicalKeysMapper.altTabPressed
418 target: applicationsDisplayLoader.item
419 property: "leftMargin"
420 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
423 target: applicationsDisplayLoader.item
424 property: "applicationManager"
425 value: ApplicationManager
428 target: applicationsDisplayLoader.item
429 property: "topLevelSurfaceList"
430 value: topLevelSurfaceList
433 target: applicationsDisplayLoader.item
434 property: "oskEnabled"
435 value: shell.oskEnabled
442 objectName: "inputMethod"
445 topMargin: panel.panelHeight
446 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
448 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
454 anchors.topMargin: panel.panelHeight
455 sourceComponent: shell.mode != "shell" ? integratedGreeter :
456 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
458 item.objectName = "greeter"
463 id: integratedGreeter
466 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
467 hides: [launcher, panel.indicators]
468 tabletMode: shell.usageScenario != "phone"
469 launcherOffset: launcher.progress
470 forcedUnlock: wizard.active || shell.mode === "full-shell"
471 background: wallpaperResolver.cachedBackground
472 hasCustomBackground: wallpaperResolver.hasCustomBackground
473 allowFingerprint: !dialogs.hasActiveDialog &&
474 !notifications.topmostIsFullscreen &&
475 !panel.indicators.shown
477 // avoid overlapping with Launcher's edge drag area
478 // FIXME: Fix TouchRegistry & friends and remove this workaround
479 // Issue involves launcher's DDA getting disabled on a long
481 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
488 if (!tutorial.running) {
493 onEmergencyCall: startLockedApp("dialer-app")
498 // See powerConnection for why this is useful
499 id: showGreeterDelayed
511 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
512 // We just received an incoming call while locked. The
513 // indicator will have already launched dialer-app for us, but
514 // there is a race between "hasCalls" changing and the dialer
515 // starting up. So in case we lose that race, we'll start/
516 // focus the dialer ourselves here too. Even if the indicator
517 // didn't launch the dialer for some reason (or maybe a call
518 // started via some other means), if an active call is
519 // happening, we want to be in the dialer.
520 startLockedApp("dialer-app")
530 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
531 !callManager.hasCalls && !wizard.active) {
532 // We don't want to simply call greeter.showNow() here, because
533 // that will take too long. Qt will delay button event
534 // handling until the greeter is done loading and may think the
535 // user held down the power button the whole time, leading to a
536 // power dialog being shown. Instead, delay showing the
537 // greeter until we've finished handling the event. We could
538 // make the greeter load asynchronously instead, but that
539 // introduces a whole host of timing issues, especially with
540 // its animations. So this is simpler.
541 showGreeterDelayed.start();
546 function showHome() {
547 greeter.notifyUserRequestedApp();
549 if (shell.mode === "greeter") {
550 SessionBroadcast.requestHomeShown(AccountsService.user);
552 var animate = !LightDM.Greeter.active && !stages.shown;
553 dash.setCurrentScope(0, animate, false);
554 ApplicationManager.requestFocusApplication("unity8-dash");
558 function showDash() {
559 if (greeter.notifyShowingDashFromDrag()) {
563 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
564 && ApplicationManager.focusedApplicationId != "unity8-dash") {
565 ApplicationManager.requestFocusApplication("unity8-dash")
579 anchors.fill: parent //because this draws indicator menus
582 available: tutorial.panelEnabled
583 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
584 && (!greeter || !greeter.hasLockedApp)
585 && !shell.waitingOnGreeter
586 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
588 minimizedPanelHeight: units.gu(3)
589 expandedPanelHeight: units.gu(7)
591 indicatorsModel: Indicators.IndicatorsModel {
592 // tablet and phone both use the same profile
593 // FIXME: use just "phone" for greeter too, but first fix
594 // greeter app launching to either load the app inside the
595 // greeter or tell the session to load the app. This will
596 // involve taking the url-dispatcher dbus name and using
597 // SessionBroadcast to tell the session.
598 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
599 Component.onCompleted: load();
604 greeterShown: greeter.shown
607 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
608 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
610 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDM.Greeter.active && launcher.progress === 0)
611 || greeter.hasLockedApp
612 locked: greeter && greeter.active
617 objectName: "launcher"
620 * Since the Dash doesn't have the same controll over surfaces that the
621 * Shell does, it can't slowly move the scope out of the way, as the shell
622 * does with apps, and the dash is show instantly. This allows for some
623 * leeway and prevents accidental home swipes.
625 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
626 readonly property bool dashSwipe: progress > offset
628 anchors.top: parent.top
629 anchors.topMargin: inverted ? 0 : panel.panelHeight
630 anchors.bottom: parent.bottom
632 dragAreaWidth: shell.edgeSize
633 available: tutorial.launcherEnabled
634 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
635 && !greeter.hasLockedApp
636 && !shell.waitingOnGreeter
637 inverted: shell.usageScenario !== "desktop"
638 superPressed: physicalKeysMapper.superPressed
639 superTabPressed: physicalKeysMapper.superTabPressed
640 panelWidth: units.gu(settings.launcherWidth)
641 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
643 onShowDashHome: showHome()
645 onDashSwipeChanged: {
647 dash.setCurrentScope(0, false, true)
650 onLauncherApplicationSelected: {
651 greeter.notifyUserRequestedApp();
652 shell.activateApplication(appId);
656 panel.indicators.hide()
661 applicationsDisplayLoader.focus = true;
666 shortcut: Qt.AltModifier | Qt.Key_F1
668 launcher.openForKeyboardNavigation();
672 shortcut: Qt.MetaModifier | Qt.Key_0
674 if (LauncherModel.get(9)) {
675 activateApplication(LauncherModel.get(9).appId);
682 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
684 if (LauncherModel.get(index)) {
685 activateApplication(LauncherModel.get(index).appId);
692 KeyboardShortcutsOverlay {
693 objectName: "shortcutsOverlay"
694 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
695 && height < parent.height - padding - panel.panelHeight
696 anchors.centerIn: parent
697 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
698 anchors.verticalCenterOffset: panel.panelHeight/2
700 opacity: enabled ? 0.95 : 0
702 Behavior on opacity {
703 UbuntuNumberAnimation {}
709 objectName: "tutorial"
712 paused: callManager.hasCalls || !greeter || greeter.active ||
714 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
715 inputMethod.visible ||
716 (launcher.shown && !launcher.lockedVisible) ||
717 panel.indicators.shown || stage.dragProgress > 0
718 usageScenario: shell.usageScenario
719 lastInputTimestamp: inputFilter.lastInputTimestamp
722 stage: applicationsDisplayLoader.item
729 deferred: shell.mode === "greeter"
731 function unlockWhenDoneWithWizard() {
733 Connectivity.unlockAllModems();
737 Component.onCompleted: unlockWhenDoneWithWizard()
738 onActiveChanged: unlockWhenDoneWithWizard()
741 MouseArea { // modal notifications prevent interacting with other contents
743 visible: notifications.useModal
750 model: NotificationBackend.Model
752 hasMouse: shell.hasMouse
753 background: wallpaperResolver.cachedBackground
755 y: topmostIsFullscreen ? 0 : panel.panelHeight
756 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
761 when: overlay.width <= units.gu(60)
763 target: notifications
764 anchors.left: parent.left
765 anchors.right: parent.right
770 when: overlay.width > units.gu(60)
772 target: notifications
773 anchors.left: undefined
774 anchors.right: parent.right
776 PropertyChanges { target: notifications; width: units.gu(38) }
784 objectName: "dialogs"
787 usageScenario: shell.usageScenario
789 shutdownFadeOutRectangle.enabled = true;
790 shutdownFadeOutRectangle.visible = true;
791 shutdownFadeOut.start();
796 target: SessionBroadcast
797 onShowHome: if (shell.mode !== "greeter") showHome()
802 objectName: "urlDispatcher"
803 active: shell.mode === "greeter"
804 onUrlRequested: shell.activateURL(url)
811 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
813 target: applicationsDisplayLoader.item
814 ignoreUnknownSignals: true
815 onItemSnapshotRequested: itemGrabber.capture(item)
821 visible: shell.hasMouse
824 property bool mouseNeverMoved: true
826 target: cursor; property: "x"; value: shell.width / 2
827 when: cursor.mouseNeverMoved && cursor.visible
830 target: cursor; property: "y"; value: shell.height / 2
831 when: cursor.mouseNeverMoved && cursor.visible
836 onPushedLeftBoundary: {
837 if (buttons === Qt.NoButton) {
838 launcher.pushEdge(amount);
842 onPushedRightBoundary: {
843 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
844 && applicationsDisplayLoader.item.pushRightEdge) {
845 applicationsDisplayLoader.item.pushRightEdge(amount);
850 mouseNeverMoved = false;
859 id: shutdownFadeOutRectangle
866 NumberAnimation on opacity {
871 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
872 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