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/>.
18 import QtQuick.Window 2.2
19 import AccountsService 0.1
20 import Unity.Application 0.1
21 import Ubuntu.Components 1.3
22 import Ubuntu.Components.Popups 1.3
23 import Ubuntu.Gestures 0.1
24 import Ubuntu.Telephony 0.1 as Telephony
25 import Unity.Connectivity 0.1
26 import Unity.Launcher 0.1
27 import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
31 import SessionBroadcast 0.1
36 import "Notifications"
40 import Unity.Notifications 1.0 as NotificationBackend
41 import Unity.Session 0.1
42 import Unity.DashCommunicator 0.1
43 import Unity.Indicators 0.1 as Indicators
45 import 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 (greeter && greeter.shown) {
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 edge
120 // tutorial or wizard while receiving a call. But a call
121 // is more important than wizard so just bail out of those.
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 // Disable everything while greeter is waiting, so that the user can't swipe
142 // the greeter or launcher until we know whether the session is locked.
143 enabled: greeter && !greeter.waiting
145 property real edgeSize: units.gu(settings.edgeDragWidth)
148 id: wallpaperResolver
152 readonly property alias greeter: greeterLoader.item
154 function activateApplication(appId) {
155 if (ApplicationManager.findApplication(appId)) {
156 ApplicationManager.requestFocusApplication(appId);
158 ApplicationManager.startApplication(appId);
162 function startLockedApp(app) {
163 if (greeter.locked) {
164 greeter.lockedApp = app;
166 shell.activateApplication(app);
170 target: LauncherModel
171 property: "applicationManager"
172 value: ApplicationManager
175 Component.onCompleted: {
176 finishStartUpTimer.start();
179 LightDM{id: lightDM} // Provide backend access
182 indicators: panel.indicators
187 objectName: "dashCommunicator"
191 id: physicalKeysMapper
192 objectName: "physicalKeysMapper"
194 onPowerKeyLongPressed: dialogs.showPowerDialog();
195 onVolumeDownTriggered: volumeControl.volumeDown();
196 onVolumeUpTriggered: volumeControl.volumeUp();
197 onScreenshotTriggered: itemGrabber.capture(shell);
201 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
206 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
207 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
211 objectName: "windowInputMonitor"
212 onHomeKeyActivated: {
213 // Ignore when greeter is active, to avoid pocket presses
214 if (!greeter.active) {
219 onTouchBegun: { cursor.opacity = 0; }
221 // move the (hidden) cursor to the last known touch position
222 var mappedCoords = mapFromItem(null, pos.x, pos.y);
223 cursor.x = mappedCoords.x;
224 cursor.y = mappedCoords.y;
230 schema.id: "com.canonical.Unity8"
237 height: parent.height
239 TopLevelSurfaceList {
240 id: topLevelSurfaceList
241 objectName: "topLevelSurfaceList"
242 applicationsModel: ApplicationManager
246 id: applicationsDisplayLoader
247 objectName: "applicationsDisplayLoader"
250 // When we have a locked app, we only want to show that one app.
251 // FIXME: do this in a less traumatic way. We currently only allow
252 // locked apps in phone mode (see FIXME in Lockscreen component in
253 // this same file). When that changes, we need to do something
254 // nicer here. But this code is currently just to prevent a
255 // theoretical attack where user enters lockedApp mode, then makes
256 // the screen larger (maybe connects to monitor) and tries to enter
259 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
261 : shell.usageScenario
262 readonly property string qmlComponent: {
263 if(shell.mode === "greeter") {
264 return "Stages/ShimStage.qml"
265 } else if (applicationsDisplayLoader.usageScenario === "phone") {
266 return "Stages/PhoneStage.qml";
267 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
268 return "Stages/TabletStage.qml";
270 return "Stages/DesktopStage.qml";
273 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
274 // Currently the new one will get loaded while the old is still hanging
275 // around for a bit, which might lead to conflicts where both stages
276 // change the model simultaneously.
277 onQmlComponentChanged: {
278 if (item) item.stageAboutToBeUnloaded();
279 source = qmlComponent;
282 property bool interactive: (!greeter || !greeter.shown)
283 && panel.indicators.fullyClosed
284 && launcher.progress == 0
285 && !notifications.useModal
287 onInteractiveChanged: { if (interactive) { focus = true; } }
290 target: applicationsDisplayLoader.item
295 target: applicationsDisplayLoader.item
296 property: "objectName"
300 target: applicationsDisplayLoader.item
301 property: "dragAreaWidth"
302 value: shell.edgeSize
305 target: applicationsDisplayLoader.item
306 property: "maximizedAppTopMargin"
307 // Not just using panel.panelHeight as that changes depending on the focused app.
308 value: panel.indicators.minimizedPanelHeight
311 target: applicationsDisplayLoader.item
312 property: "interactive"
313 value: applicationsDisplayLoader.interactive
316 target: applicationsDisplayLoader.item
317 property: "spreadEnabled"
318 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
321 target: applicationsDisplayLoader.item
322 property: "inverseProgress"
323 value: greeter && greeter.locked ? 0 : launcher.progress
326 target: applicationsDisplayLoader.item
327 property: "shellOrientationAngle"
328 value: shell.orientationAngle
331 target: applicationsDisplayLoader.item
332 property: "shellOrientation"
333 value: shell.orientation
336 target: applicationsDisplayLoader.item
337 property: "orientations"
338 value: shell.orientations
341 target: applicationsDisplayLoader.item
342 property: "background"
343 value: wallpaperResolver.background
346 target: applicationsDisplayLoader.item
347 property: "nativeWidth"
348 value: shell.nativeWidth
351 target: applicationsDisplayLoader.item
352 property: "nativeHeight"
353 value: shell.nativeHeight
356 target: applicationsDisplayLoader.item
357 property: "beingResized"
358 value: shell.beingResized
361 target: applicationsDisplayLoader.item
362 property: "keepDashRunning"
363 value: launcher.shown || launcher.dashSwipe
366 target: applicationsDisplayLoader.item
367 property: "suspended"
371 target: applicationsDisplayLoader.item
372 property: "altTabPressed"
373 value: physicalKeysMapper.altTabPressed
376 target: applicationsDisplayLoader.item
377 property: "leftMargin"
378 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
381 target: applicationsDisplayLoader.item
382 property: "applicationManager"
383 value: ApplicationManager
386 target: applicationsDisplayLoader.item
387 property: "topLevelSurfaceList"
388 value: topLevelSurfaceList
395 objectName: "inputMethod"
398 topMargin: panel.panelHeight
399 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
401 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
407 anchors.topMargin: panel.panelHeight
408 sourceComponent: shell.mode != "shell" ? integratedGreeter :
409 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
411 item.objectName = "greeter"
416 id: integratedGreeter
419 hides: [launcher, panel.indicators]
420 tabletMode: shell.usageScenario != "phone"
421 launcherOffset: launcher.progress
422 forcedUnlock: wizard.active
423 background: wallpaperResolver.background
425 // avoid overlapping with Launcher's edge drag area
426 // FIXME: Fix TouchRegistry & friends and remove this workaround
427 // Issue involves launcher's DDA getting disabled on a long
429 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
436 if (!tutorial.running) {
441 onEmergencyCall: startLockedApp("dialer-app")
446 // See powerConnection for why this is useful
447 id: showGreeterDelayed
459 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
460 // We just received an incoming call while locked. The
461 // indicator will have already launched dialer-app for us, but
462 // there is a race between "hasCalls" changing and the dialer
463 // starting up. So in case we lose that race, we'll start/
464 // focus the dialer ourselves here too. Even if the indicator
465 // didn't launch the dialer for some reason (or maybe a call
466 // started via some other means), if an active call is
467 // happening, we want to be in the dialer.
468 startLockedApp("dialer-app")
478 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
479 !callManager.hasCalls && !wizard.active) {
480 // We don't want to simply call greeter.showNow() here, because
481 // that will take too long. Qt will delay button event
482 // handling until the greeter is done loading and may think the
483 // user held down the power button the whole time, leading to a
484 // power dialog being shown. Instead, delay showing the
485 // greeter until we've finished handling the event. We could
486 // make the greeter load asynchronously instead, but that
487 // introduces a whole host of timing issues, especially with
488 // its animations. So this is simpler.
489 showGreeterDelayed.start();
494 function showHome() {
495 greeter.notifyUserRequestedApp("unity8-dash");
497 var animate = !lightDM.greeter.active && !stages.shown
498 dash.setCurrentScope(0, animate, false)
499 ApplicationManager.requestFocusApplication("unity8-dash")
502 function showDash() {
503 if (greeter.notifyShowingDashFromDrag()) {
507 if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") {
508 ApplicationManager.requestFocusApplication("unity8-dash")
522 anchors.fill: parent //because this draws indicator menus
525 available: tutorial.panelEnabled
526 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
527 && (!greeter || !greeter.hasLockedApp)
528 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
530 minimizedPanelHeight: units.gu(3)
531 expandedPanelHeight: units.gu(7)
533 indicatorsModel: Indicators.IndicatorsModel {
534 // tablet and phone both use the same profile
536 Component.onCompleted: load();
541 greeterShown: greeter.shown
544 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
545 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
547 fullscreenMode: (focusedSurfaceIsFullscreen && !lightDM.greeter.active && launcher.progress == 0)
548 || greeter.hasLockedApp
549 locked: greeter && greeter.active
554 objectName: "launcher"
556 readonly property bool dashSwipe: progress > 0
558 anchors.top: parent.top
559 anchors.topMargin: inverted ? 0 : panel.panelHeight
560 anchors.bottom: parent.bottom
562 dragAreaWidth: shell.edgeSize
563 available: tutorial.launcherEnabled
564 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
565 && !greeter.hasLockedApp
566 inverted: shell.usageScenario !== "desktop"
567 superPressed: physicalKeysMapper.superPressed
568 superTabPressed: physicalKeysMapper.superTabPressed
569 panelWidth: units.gu(settings.launcherWidth)
570 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
572 onShowDashHome: showHome()
574 onDashSwipeChanged: {
576 dash.setCurrentScope(0, false, true)
579 onLauncherApplicationSelected: {
580 greeter.notifyUserRequestedApp(appId);
581 shell.activateApplication(appId);
585 panel.indicators.hide()
590 applicationsDisplayLoader.focus = true;
595 shortcut: Qt.AltModifier | Qt.Key_F1
597 launcher.openForKeyboardNavigation();
601 shortcut: Qt.MetaModifier | Qt.Key_0
603 if (LauncherModel.get(9)) {
604 activateApplication(LauncherModel.get(9).appId);
611 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
613 if (LauncherModel.get(index)) {
614 activateApplication(LauncherModel.get(index).appId);
623 objectName: "tutorial"
626 paused: callManager.hasCalls || greeter.shown
627 keyboardVisible: inputMethod.state === "shown"
628 usageScenario: shell.usageScenario
629 lastInputTimestamp: inputFilter.lastInputTimestamp
632 stage: applicationsDisplayLoader.item
640 function unlockWhenDoneWithWizard() {
642 Connectivity.unlockAllModems();
646 Component.onCompleted: unlockWhenDoneWithWizard()
647 onActiveChanged: unlockWhenDoneWithWizard()
650 MouseArea { // modal notifications prevent interacting with other contents
652 visible: notifications.useModal
659 model: NotificationBackend.Model
661 hasMouse: shell.hasMouse
662 background: wallpaperResolver.background
664 y: topmostIsFullscreen ? 0 : panel.panelHeight
665 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
670 when: overlay.width <= units.gu(60)
672 target: notifications
673 anchors.left: parent.left
674 anchors.right: parent.right
679 when: overlay.width > units.gu(60)
681 target: notifications
682 anchors.left: undefined
683 anchors.right: parent.right
685 PropertyChanges { target: notifications; width: units.gu(38) }
693 objectName: "dialogs"
696 usageScenario: shell.usageScenario
698 shutdownFadeOutRectangle.enabled = true;
699 shutdownFadeOutRectangle.visible = true;
700 shutdownFadeOut.start();
705 target: SessionBroadcast
706 onShowHome: showHome()
713 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
715 target: applicationsDisplayLoader.item
716 ignoreUnknownSignals: true
717 onItemSnapshotRequested: itemGrabber.capture(item)
723 visible: shell.hasMouse
726 onPushedLeftBoundary: {
727 if (buttons === Qt.NoButton) {
728 launcher.pushEdge(amount);
732 onPushedRightBoundary: {
733 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
734 && applicationsDisplayLoader.item.pushRightEdge) {
735 applicationsDisplayLoader.item.pushRightEdge(amount);
739 onMouseMoved: { cursor.opacity = 1; }
746 id: shutdownFadeOutRectangle
753 NumberAnimation on opacity {
758 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
759 DBusUnitySessionService.shutdown();