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
31import SessionBroadcast 0.1
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.DashCommunicator 0.1
43import Unity.Indicators 0.1 as Indicators
45import WindowManager 0.1
51 theme.name: "Ubuntu.Components.Themes.SuruDark"
53 // to be set from outside
54 property int orientationAngle: 0
55 property int orientation
56 property Orientations orientations
57 property real nativeWidth
58 property real nativeHeight
59 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
60 property bool beingResized
61 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
62 property string mode: "full-greeter"
63 property alias oskEnabled: inputMethod.enabled
64 function updateFocusedAppOrientation() {
65 applicationsDisplayLoader.item.updateFocusedAppOrientation();
67 function updateFocusedAppOrientationAnimated() {
68 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
70 property bool hasMouse: false
72 // to be read from outside
73 readonly property int mainAppWindowOrientationAngle:
74 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
76 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
77 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
78 && (!greeter || !greeter.animating)
80 readonly property bool showingGreeter: greeter && greeter.shown
82 property bool startingUp: true
83 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
85 property int supportedOrientations: {
87 // Ensure we don't rotate during start up
88 return Qt.PrimaryOrientation;
89 } else if (showingGreeter || notifications.topmostIsFullscreen) {
90 return Qt.PrimaryOrientation;
91 } else if (applicationsDisplayLoader.item) {
92 return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
95 return Qt.PortraitOrientation
96 | Qt.LandscapeOrientation
97 | Qt.InvertedPortraitOrientation
98 | Qt.InvertedLandscapeOrientation;
102 readonly property var mainApp:
103 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
106 _onMainAppChanged(mainApp.appId);
110 target: ApplicationManager
112 if (shell.mainApp && shell.mainApp.appId === appId) {
113 _onMainAppChanged(appId);
117 function _onMainAppChanged(appId) {
118 if (wizard.active && appId != "" && appId != "unity8-dash") {
119 // If this happens on first boot, we may be in the
120 // wizard while receiving a call. But a call is more
121 // important than the wizard so just bail out of it.
125 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
126 // If we are in the middle of a call, make dialer lockedApp and show it.
127 // This can happen if user backs out of dialer back to greeter, then
128 // launches dialer again.
129 greeter.lockedApp = appId;
131 greeter.notifyAppFocusRequested(appId);
133 panel.indicators.hide();
137 // For autopilot consumption
138 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
140 // Note when greeter is waiting on PAM, so that we can disable edges until
141 // we know which user data to show and whether the session is locked.
142 readonly property bool waitingOnGreeter: greeter && greeter.waiting
144 property real edgeSize: units.gu(settings.edgeDragWidth)
147 id: wallpaperResolver
151 readonly property alias greeter: greeterLoader.item
153 function activateApplication(appId) {
154 // Either open the app in our own session, or -- if we're acting as a
155 // greeter -- ask the user's session to open it for us.
156 if (shell.mode === "greeter") {
157 activateURL("application:///" + appId + ".desktop");
163 function activateURL(url) {
164 SessionBroadcast.requestUrlStart(AccountsService.user, url);
165 greeter.notifyUserRequestedApp();
166 panel.indicators.hide();
169 function startApp(appId) {
170 if (ApplicationManager.findApplication(appId)) {
171 ApplicationManager.requestFocusApplication(appId);
173 ApplicationManager.startApplication(appId);
177 function startLockedApp(app) {
178 if (greeter.locked) {
179 greeter.lockedApp = app;
181 startApp(app); // locked apps are always in our same session
185 target: LauncherModel
186 property: "applicationManager"
187 value: ApplicationManager
190 Component.onCompleted: {
191 finishStartUpTimer.start();
200 objectName: "dashCommunicator"
204 id: physicalKeysMapper
205 objectName: "physicalKeysMapper"
207 onPowerKeyLongPressed: dialogs.showPowerDialog();
208 onVolumeDownTriggered: volumeControl.volumeDown();
209 onVolumeUpTriggered: volumeControl.volumeUp();
210 onScreenshotTriggered: itemGrabber.capture(shell);
214 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
219 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
220 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
224 objectName: "windowInputMonitor"
225 onHomeKeyActivated: {
226 // Ignore when greeter is active, to avoid pocket presses
227 if (!greeter.active) {
232 onTouchBegun: { cursor.opacity = 0; }
234 // move the (hidden) cursor to the last known touch position
235 var mappedCoords = mapFromItem(null, pos.x, pos.y);
236 cursor.x = mappedCoords.x;
237 cursor.y = mappedCoords.y;
238 cursor.mouseNeverMoved = false;
244 schema.id: "com.canonical.Unity8"
251 height: parent.height
253 TopLevelSurfaceList {
254 id: topLevelSurfaceList
255 objectName: "topLevelSurfaceList"
256 applicationsModel: ApplicationManager
260 id: applicationsDisplayLoader
261 objectName: "applicationsDisplayLoader"
264 // When we have a locked app, we only want to show that one app.
265 // FIXME: do this in a less traumatic way. We currently only allow
266 // locked apps in phone mode (see FIXME in Lockscreen component in
267 // this same file). When that changes, we need to do something
268 // nicer here. But this code is currently just to prevent a
269 // theoretical attack where user enters lockedApp mode, then makes
270 // the screen larger (maybe connects to monitor) and tries to enter
273 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
275 : shell.usageScenario
276 readonly property string qmlComponent: {
277 if (applicationsDisplayLoader.usageScenario === "phone") {
278 return "Stages/PhoneStage.qml";
279 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
280 return "Stages/TabletStage.qml";
282 return "Stages/DesktopStage.qml";
285 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
286 // Currently the new one will get loaded while the old is still hanging
287 // around for a bit, which might lead to conflicts where both stages
288 // change the model simultaneously.
289 onQmlComponentChanged: {
290 if (item) item.stageAboutToBeUnloaded();
291 source = qmlComponent;
294 property bool interactive: (!greeter || !greeter.shown)
295 && panel.indicators.fullyClosed
296 && launcher.progress == 0
297 && !notifications.useModal
299 onInteractiveChanged: { if (interactive) { focus = true; } }
302 target: applicationsDisplayLoader.item
307 target: applicationsDisplayLoader.item
308 property: "objectName"
312 target: applicationsDisplayLoader.item
313 property: "dragAreaWidth"
314 value: shell.edgeSize
317 target: applicationsDisplayLoader.item
318 property: "maximizedAppTopMargin"
319 // Not just using panel.panelHeight as that changes depending on the focused app.
320 value: panel.indicators.minimizedPanelHeight
323 target: applicationsDisplayLoader.item
324 property: "interactive"
325 value: applicationsDisplayLoader.interactive
328 target: applicationsDisplayLoader.item
329 property: "spreadEnabled"
330 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
333 target: applicationsDisplayLoader.item
334 property: "inverseProgress"
335 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
338 target: applicationsDisplayLoader.item
339 property: "shellOrientationAngle"
340 value: shell.orientationAngle
343 target: applicationsDisplayLoader.item
344 property: "shellOrientation"
345 value: shell.orientation
348 target: applicationsDisplayLoader.item
349 property: "orientations"
350 value: shell.orientations
353 target: applicationsDisplayLoader.item
354 property: "background"
355 value: wallpaperResolver.background
358 target: applicationsDisplayLoader.item
359 property: "nativeWidth"
360 value: shell.nativeWidth
363 target: applicationsDisplayLoader.item
364 property: "nativeHeight"
365 value: shell.nativeHeight
368 target: applicationsDisplayLoader.item
369 property: "beingResized"
370 value: shell.beingResized
373 target: applicationsDisplayLoader.item
374 property: "keepDashRunning"
375 value: launcher.shown || launcher.dashSwipe
378 target: applicationsDisplayLoader.item
379 property: "suspended"
383 target: applicationsDisplayLoader.item
384 property: "altTabPressed"
385 value: physicalKeysMapper.altTabPressed
388 target: applicationsDisplayLoader.item
389 property: "leftMargin"
390 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
393 target: applicationsDisplayLoader.item
394 property: "applicationManager"
395 value: ApplicationManager
398 target: applicationsDisplayLoader.item
399 property: "topLevelSurfaceList"
400 value: topLevelSurfaceList
403 target: applicationsDisplayLoader.item
404 property: "oskEnabled"
405 value: shell.oskEnabled
412 objectName: "inputMethod"
415 topMargin: panel.panelHeight
416 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
418 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
424 anchors.topMargin: panel.panelHeight
425 sourceComponent: shell.mode != "shell" ? integratedGreeter :
426 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
428 item.objectName = "greeter"
433 id: integratedGreeter
436 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
437 hides: [launcher, panel.indicators]
438 tabletMode: shell.usageScenario != "phone"
439 launcherOffset: launcher.progress
440 forcedUnlock: wizard.active || shell.mode === "full-shell"
441 background: wallpaperResolver.background
442 allowFingerprint: !dialogs.hasActiveDialog &&
443 !notifications.topmostIsFullscreen &&
444 !panel.indicators.shown
446 // avoid overlapping with Launcher's edge drag area
447 // FIXME: Fix TouchRegistry & friends and remove this workaround
448 // Issue involves launcher's DDA getting disabled on a long
450 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
457 if (!tutorial.running) {
462 onEmergencyCall: startLockedApp("dialer-app")
467 // See powerConnection for why this is useful
468 id: showGreeterDelayed
480 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
481 // We just received an incoming call while locked. The
482 // indicator will have already launched dialer-app for us, but
483 // there is a race between "hasCalls" changing and the dialer
484 // starting up. So in case we lose that race, we'll start/
485 // focus the dialer ourselves here too. Even if the indicator
486 // didn't launch the dialer for some reason (or maybe a call
487 // started via some other means), if an active call is
488 // happening, we want to be in the dialer.
489 startLockedApp("dialer-app")
499 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
500 !callManager.hasCalls && !wizard.active) {
501 // We don't want to simply call greeter.showNow() here, because
502 // that will take too long. Qt will delay button event
503 // handling until the greeter is done loading and may think the
504 // user held down the power button the whole time, leading to a
505 // power dialog being shown. Instead, delay showing the
506 // greeter until we've finished handling the event. We could
507 // make the greeter load asynchronously instead, but that
508 // introduces a whole host of timing issues, especially with
509 // its animations. So this is simpler.
510 showGreeterDelayed.start();
515 function showHome() {
516 greeter.notifyUserRequestedApp();
518 if (shell.mode === "greeter") {
519 SessionBroadcast.requestHomeShown(AccountsService.user);
521 var animate = !LightDMService.greeter.active && !stages.shown;
522 dash.setCurrentScope(0, animate, false);
523 ApplicationManager.requestFocusApplication("unity8-dash");
527 function showDash() {
528 if (greeter.notifyShowingDashFromDrag()) {
532 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
533 && ApplicationManager.focusedApplicationId != "unity8-dash") {
534 ApplicationManager.requestFocusApplication("unity8-dash")
548 anchors.fill: parent //because this draws indicator menus
551 available: tutorial.panelEnabled
552 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
553 && (!greeter || !greeter.hasLockedApp)
554 && !shell.waitingOnGreeter
555 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
557 minimizedPanelHeight: units.gu(3)
558 expandedPanelHeight: units.gu(7)
560 indicatorsModel: Indicators.IndicatorsModel {
561 // tablet and phone both use the same profile
562 // FIXME: use just "phone" for greeter too, but first fix
563 // greeter app launching to either load the app inside the
564 // greeter or tell the session to load the app. This will
565 // involve taking the url-dispatcher dbus name and using
566 // SessionBroadcast to tell the session.
567 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
568 Component.onCompleted: load();
573 greeterShown: greeter.shown
576 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
577 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
579 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
580 || greeter.hasLockedApp
581 locked: greeter && greeter.active
586 objectName: "launcher"
589 * Since the Dash doesn't have the same controll over surfaces that the
590 * Shell does, it can't slowly move the scope out of the way, as the shell
591 * does with apps, and the dash is show instantly. This allows for some
592 * leeway and prevents accidental home swipes.
594 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
595 readonly property bool dashSwipe: progress > offset
597 anchors.top: parent.top
598 anchors.topMargin: inverted ? 0 : panel.panelHeight
599 anchors.bottom: parent.bottom
601 dragAreaWidth: shell.edgeSize
602 available: tutorial.launcherEnabled
603 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
604 && !greeter.hasLockedApp
605 && !shell.waitingOnGreeter
606 inverted: shell.usageScenario !== "desktop"
607 superPressed: physicalKeysMapper.superPressed
608 superTabPressed: physicalKeysMapper.superTabPressed
609 panelWidth: units.gu(settings.launcherWidth)
610 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
612 onShowDashHome: showHome()
614 onDashSwipeChanged: {
616 dash.setCurrentScope(0, false, true)
619 onLauncherApplicationSelected: {
620 greeter.notifyUserRequestedApp();
621 shell.activateApplication(appId);
625 panel.indicators.hide()
630 applicationsDisplayLoader.focus = true;
635 shortcut: Qt.AltModifier | Qt.Key_F1
637 launcher.openForKeyboardNavigation();
641 shortcut: Qt.MetaModifier | Qt.Key_0
643 if (LauncherModel.get(9)) {
644 activateApplication(LauncherModel.get(9).appId);
651 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
653 if (LauncherModel.get(index)) {
654 activateApplication(LauncherModel.get(index).appId);
661 KeyboardShortcutsOverlay {
662 objectName: "shortcutsOverlay"
663 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
664 && height < parent.height - padding - panel.panelHeight
665 anchors.centerIn: parent
666 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
667 anchors.verticalCenterOffset: panel.panelHeight/2
669 opacity: enabled ? 0.95 : 0
671 Behavior on opacity {
672 UbuntuNumberAnimation {}
678 objectName: "tutorial"
681 paused: callManager.hasCalls || !greeter || greeter.active ||
683 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
684 inputMethod.visible ||
685 (launcher.shown && !launcher.lockedVisible) ||
686 panel.indicators.shown || stage.dragProgress > 0
687 usageScenario: shell.usageScenario
688 lastInputTimestamp: inputFilter.lastInputTimestamp
691 stage: applicationsDisplayLoader.item
698 deferred: shell.mode === "greeter"
700 function unlockWhenDoneWithWizard() {
702 Connectivity.unlockAllModems();
706 Component.onCompleted: unlockWhenDoneWithWizard()
707 onActiveChanged: unlockWhenDoneWithWizard()
710 MouseArea { // modal notifications prevent interacting with other contents
712 visible: notifications.useModal
719 model: NotificationBackend.Model
721 hasMouse: shell.hasMouse
722 background: wallpaperResolver.background
724 y: topmostIsFullscreen ? 0 : panel.panelHeight
725 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
730 when: overlay.width <= units.gu(60)
732 target: notifications
733 anchors.left: parent.left
734 anchors.right: parent.right
739 when: overlay.width > units.gu(60)
741 target: notifications
742 anchors.left: undefined
743 anchors.right: parent.right
745 PropertyChanges { target: notifications; width: units.gu(38) }
753 objectName: "dialogs"
756 usageScenario: shell.usageScenario
758 shutdownFadeOutRectangle.enabled = true;
759 shutdownFadeOutRectangle.visible = true;
760 shutdownFadeOut.start();
765 target: SessionBroadcast
766 onShowHome: if (shell.mode !== "greeter") showHome()
771 objectName: "urlDispatcher"
772 active: shell.mode === "greeter"
773 onUrlRequested: shell.activateURL(url)
780 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
782 target: applicationsDisplayLoader.item
783 ignoreUnknownSignals: true
784 onItemSnapshotRequested: itemGrabber.capture(item)
790 visible: shell.hasMouse
793 property bool mouseNeverMoved: true
795 target: cursor; property: "x"; value: shell.width / 2
796 when: cursor.mouseNeverMoved && cursor.visible
799 target: cursor; property: "y"; value: shell.height / 2
800 when: cursor.mouseNeverMoved && cursor.visible
805 onPushedLeftBoundary: {
806 if (buttons === Qt.NoButton) {
807 launcher.pushEdge(amount);
811 onPushedRightBoundary: {
812 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
813 && applicationsDisplayLoader.item.pushRightEdge) {
814 applicationsDisplayLoader.item.pushRightEdge(amount);
819 mouseNeverMoved = false;
828 id: shutdownFadeOutRectangle
835 NumberAnimation on opacity {
840 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
841 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