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
50 // to be set from outside
51 property int orientationAngle: 0
52 property int orientation
53 property Orientations orientations
54 property real nativeWidth
55 property real nativeHeight
56 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
57 property bool beingResized
58 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
59 property string mode: "full-greeter"
60 property alias oskEnabled: inputMethod.enabled
61 function updateFocusedAppOrientation() {
62 applicationsDisplayLoader.item.updateFocusedAppOrientation();
64 function updateFocusedAppOrientationAnimated() {
65 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
67 property bool hasMouse: false
69 // to be read from outside
70 readonly property int mainAppWindowOrientationAngle:
71 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
73 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
74 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
75 && (!greeter || !greeter.animating)
77 readonly property bool showingGreeter: greeter && greeter.shown
79 property bool startingUp: true
80 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
82 property int supportedOrientations: {
84 // Ensure we don't rotate during start up
85 return Qt.PrimaryOrientation;
86 } else if (greeter && greeter.shown) {
87 return Qt.PrimaryOrientation;
88 } else if (applicationsDisplayLoader.item) {
89 return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
92 return Qt.PortraitOrientation
93 | Qt.LandscapeOrientation
94 | Qt.InvertedPortraitOrientation
95 | Qt.InvertedLandscapeOrientation;
99 // For autopilot consumption
100 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
102 // internal props from here onwards
103 readonly property var mainApp:
104 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
105 readonly property var mainAppWindow:
106 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindow : null
108 // Disable everything while greeter is waiting, so that the user can't swipe
109 // the greeter or launcher until we know whether the session is locked.
110 enabled: greeter && !greeter.waiting
112 property real edgeSize: units.gu(2)
115 id: wallpaperResolver
119 readonly property alias greeter: greeterLoader.item
121 function activateApplication(appId) {
122 if (ApplicationManager.findApplication(appId)) {
123 ApplicationManager.requestFocusApplication(appId);
125 ApplicationManager.startApplication(appId);
129 function startLockedApp(app) {
130 if (greeter.locked) {
131 greeter.lockedApp = app;
133 shell.activateApplication(app);
137 target: LauncherModel
138 property: "applicationManager"
139 value: ApplicationManager
142 Component.onCompleted: {
143 theme.name = "Ubuntu.Components.Themes.SuruDark"
144 if (ApplicationManager.count > 0) {
145 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
147 finishStartUpTimer.start();
150 LightDM{id: lightDM} // Provide backend access
153 indicators: panel.indicators
158 objectName: "dashCommunicator"
162 id: physicalKeysMapper
163 objectName: "physicalKeysMapper"
165 onPowerKeyLongPressed: dialogs.showPowerDialog();
166 onVolumeDownTriggered: volumeControl.volumeDown();
167 onVolumeUpTriggered: volumeControl.volumeUp();
168 onScreenshotTriggered: screenGrabber.capture();
172 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
177 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
178 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
182 onHomeKeyActivated: { launcher.fadeOut(); shell.showHome(); }
183 onTouchBegun: { cursor.opacity = 0; }
185 // move the (hidden) cursor to the last known touch position
186 var mappedCoords = mapFromItem(null, pos.x, pos.y);
187 cursor.x = mappedCoords.x;
188 cursor.y = mappedCoords.y;
194 schema.id: "com.canonical.Unity8"
201 height: parent.height
204 target: ApplicationManager
206 // This signal is also fired when we try to focus the current app
207 // again. We rely on this!
208 onFocusedApplicationIdChanged: {
209 var appId = ApplicationManager.focusedApplicationId;
211 if (wizard.active && appId != "" && appId != "unity8-dash") {
212 // If this happens on first boot, we may be in edge
213 // tutorial or wizard while receiving a call. But a call
214 // is more important than wizard so just bail out of those.
219 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
220 // If we are in the middle of a call, make dialer lockedApp and show it.
221 // This can happen if user backs out of dialer back to greeter, then
222 // launches dialer again.
223 greeter.lockedApp = appId;
225 greeter.notifyAppFocused(appId);
227 panel.indicators.hide();
230 onApplicationAdded: {
236 id: applicationsDisplayLoader
237 objectName: "applicationsDisplayLoader"
240 // When we have a locked app, we only want to show that one app.
241 // FIXME: do this in a less traumatic way. We currently only allow
242 // locked apps in phone mode (see FIXME in Lockscreen component in
243 // this same file). When that changes, we need to do something
244 // nicer here. But this code is currently just to prevent a
245 // theoretical attack where user enters lockedApp mode, then makes
246 // the screen larger (maybe connects to monitor) and tries to enter
249 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
251 : shell.usageScenario
252 readonly property string qmlComponent: {
253 if(shell.mode === "greeter") {
254 return "Stages/ShimStage.qml"
255 } else if (applicationsDisplayLoader.usageScenario === "phone") {
256 return "Stages/PhoneStage.qml";
257 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
258 return "Stages/TabletStage.qml";
260 return "Stages/DesktopStage.qml";
263 onQmlComponentChanged: {
264 if (item) item.stageAboutToBeUnloaded();
265 source = qmlComponent;
268 property bool interactive: (!greeter || !greeter.shown)
269 && panel.indicators.fullyClosed
270 && launcher.progress == 0
271 && !notifications.useModal
273 onInteractiveChanged: { if (interactive) { focus = true; } }
276 target: applicationsDisplayLoader.item
277 property: "objectName"
281 target: applicationsDisplayLoader.item
282 property: "dragAreaWidth"
283 value: shell.edgeSize
286 target: applicationsDisplayLoader.item
287 property: "maximizedAppTopMargin"
288 // Not just using panel.panelHeight as that changes depending on the focused app.
289 value: panel.indicators.minimizedPanelHeight
292 target: applicationsDisplayLoader.item
293 property: "interactive"
294 value: applicationsDisplayLoader.interactive
297 target: applicationsDisplayLoader.item
298 property: "spreadEnabled"
299 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
302 target: applicationsDisplayLoader.item
303 property: "inverseProgress"
304 value: greeter && greeter.locked ? 0 : launcher.progress
307 target: applicationsDisplayLoader.item
308 property: "shellOrientationAngle"
309 value: shell.orientationAngle
312 target: applicationsDisplayLoader.item
313 property: "shellOrientation"
314 value: shell.orientation
317 target: applicationsDisplayLoader.item
318 property: "orientations"
319 value: shell.orientations
322 target: applicationsDisplayLoader.item
323 property: "background"
324 value: wallpaperResolver.background
327 target: applicationsDisplayLoader.item
328 property: "nativeWidth"
329 value: shell.nativeWidth
332 target: applicationsDisplayLoader.item
333 property: "nativeHeight"
334 value: shell.nativeHeight
337 target: applicationsDisplayLoader.item
338 property: "beingResized"
339 value: shell.beingResized
342 target: applicationsDisplayLoader.item
343 property: "keepDashRunning"
344 value: launcher.shown || launcher.dashSwipe
347 target: applicationsDisplayLoader.item
348 property: "suspended"
352 target: applicationsDisplayLoader.item
353 property: "altTabPressed"
354 value: physicalKeysMapper.altTabPressed
357 target: applicationsDisplayLoader.item
358 property: "leftMargin"
359 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
366 objectName: "inputMethod"
369 topMargin: panel.panelHeight
370 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
372 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
376 target: SessionManager
378 if (!session.parentSession && !session.application) {
379 // nothing is using it. delete it right away
388 anchors.topMargin: panel.panelHeight
389 sourceComponent: shell.mode != "shell" ? integratedGreeter :
390 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
392 item.objectName = "greeter"
397 id: integratedGreeter
400 hides: [launcher, panel.indicators]
401 tabletMode: shell.usageScenario != "phone"
402 launcherOffset: launcher.progress
403 forcedUnlock: wizard.active
404 background: wallpaperResolver.background
406 // avoid overlapping with Launcher's edge drag area
407 // FIXME: Fix TouchRegistry & friends and remove this workaround
408 // Issue involves launcher's DDA getting disabled on a long
410 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
417 if (!tutorial.running) {
422 onEmergencyCall: startLockedApp("dialer-app")
427 // See powerConnection for why this is useful
428 id: showGreeterDelayed
440 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
441 // We just received an incoming call while locked. The
442 // indicator will have already launched dialer-app for us, but
443 // there is a race between "hasCalls" changing and the dialer
444 // starting up. So in case we lose that race, we'll start/
445 // focus the dialer ourselves here too. Even if the indicator
446 // didn't launch the dialer for some reason (or maybe a call
447 // started via some other means), if an active call is
448 // happening, we want to be in the dialer.
449 startLockedApp("dialer-app")
459 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
460 !callManager.hasCalls && !wizard.active) {
461 // We don't want to simply call greeter.showNow() here, because
462 // that will take too long. Qt will delay button event
463 // handling until the greeter is done loading and may think the
464 // user held down the power button the whole time, leading to a
465 // power dialog being shown. Instead, delay showing the
466 // greeter until we've finished handling the event. We could
467 // make the greeter load asynchronously instead, but that
468 // introduces a whole host of timing issues, especially with
469 // its animations. So this is simpler.
470 showGreeterDelayed.start();
475 function showHome() {
476 greeter.notifyAboutToFocusApp("unity8-dash");
478 var animate = !lightDM.greeter.active && !stages.shown
479 dash.setCurrentScope(0, animate, false)
480 ApplicationManager.requestFocusApplication("unity8-dash")
483 function showDash() {
484 if (greeter.notifyShowingDashFromDrag()) {
488 if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") {
489 ApplicationManager.requestFocusApplication("unity8-dash")
503 anchors.fill: parent //because this draws indicator menus
506 available: tutorial.panelEnabled
507 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
508 && (!greeter || !greeter.hasLockedApp)
509 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
511 minimizedPanelHeight: units.gu(3)
512 expandedPanelHeight: units.gu(7)
514 indicatorsModel: Indicators.IndicatorsModel {
515 // tablet and phone both use the same profile
517 Component.onCompleted: load();
522 greeterShown: greeter.shown
525 readonly property bool topmostApplicationIsFullscreen: mainApp && mainApp.fullscreen
527 fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0)
528 || greeter.hasLockedApp
529 locked: greeter && greeter.active
534 objectName: "launcher"
536 readonly property bool dashSwipe: progress > 0
538 anchors.top: parent.top
539 anchors.topMargin: inverted ? 0 : panel.panelHeight
540 anchors.bottom: parent.bottom
542 dragAreaWidth: shell.edgeSize
543 available: tutorial.launcherEnabled
544 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
545 && !greeter.hasLockedApp
546 inverted: shell.usageScenario !== "desktop"
547 superPressed: physicalKeysMapper.superPressed
548 superTabPressed: physicalKeysMapper.superTabPressed
549 panelWidth: units.gu(settings.launcherWidth)
550 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
552 onShowDashHome: showHome()
554 onDashSwipeChanged: {
556 dash.setCurrentScope(0, false, true)
559 onLauncherApplicationSelected: {
560 greeter.notifyAboutToFocusApp(appId);
561 shell.activateApplication(appId);
565 panel.indicators.hide()
570 applicationsDisplayLoader.focus = true;
575 shortcut: Qt.AltModifier | Qt.Key_F1
577 launcher.openForKeyboardNavigation();
581 shortcut: Qt.MetaModifier | Qt.Key_0
583 if (LauncherModel.get(9)) {
584 activateApplication(LauncherModel.get(9).appId);
591 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
593 if (LauncherModel.get(index)) {
594 activateApplication(LauncherModel.get(index).appId);
603 objectName: "tutorial"
606 paused: callManager.hasCalls || greeter.shown
607 keyboardVisible: inputMethod.state === "shown"
608 usageScenario: shell.usageScenario
609 lastInputTimestamp: inputFilter.lastInputTimestamp
612 stage: applicationsDisplayLoader.item
620 function unlockWhenDoneWithWizard() {
622 Connectivity.unlockAllModems();
626 Component.onCompleted: unlockWhenDoneWithWizard()
627 onActiveChanged: unlockWhenDoneWithWizard()
631 id: modalNotificationBackground
633 visible: notifications.useModal
646 model: NotificationBackend.Model
648 hasMouse: shell.hasMouse
649 inverseMode: panel.indicators.shown
650 background: wallpaperResolver.background
652 y: topmostIsFullscreen ? 0 : panel.panelHeight
653 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
658 when: overlay.width <= units.gu(60)
660 target: notifications
661 anchors.left: parent.left
662 anchors.right: parent.right
667 when: overlay.width > units.gu(60)
669 target: notifications
670 anchors.left: undefined
671 anchors.right: parent.right
673 PropertyChanges { target: notifications; width: units.gu(38) }
681 objectName: "dialogs"
684 usageScenario: shell.usageScenario
686 shutdownFadeOutRectangle.enabled = true;
687 shutdownFadeOutRectangle.visible = true;
688 shutdownFadeOut.start();
693 target: SessionBroadcast
694 onShowHome: showHome()
699 rotationAngle: -shell.orientationAngle
705 visible: shell.hasMouse
706 z: screenGrabber.z + 1
708 onPushedLeftBoundary: {
709 if (buttons === Qt.NoButton) {
710 launcher.pushEdge(amount);
714 onPushedRightBoundary: {
715 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
716 && applicationsDisplayLoader.item.pushRightEdge) {
717 applicationsDisplayLoader.item.pushRightEdge(amount);
721 onMouseMoved: { cursor.opacity = 1; }
726 shortcut: Qt.MetaModifier|Qt.Key_Space
727 onTriggered: keymapPriv.nextKeymap()
728 active: keymapPriv.keymapCount > 1
732 shortcut: Qt.MetaModifier|Qt.ShiftModifier|Qt.Key_Space
733 onTriggered: keymapPriv.previousKeymap()
734 active: keymapPriv.keymapCount > 1
740 readonly property var keymaps: AccountsService.keymaps
741 readonly property int keymapCount: keymaps.length
742 property int currentKeymapIndex: 0 // the new one that we're setting
743 onCurrentKeymapIndexChanged: switchToKeymap();
745 function nextKeymap() {
748 if (currentKeymapIndex !== -1 && currentKeymapIndex < keymapCount - 1) {
749 nextIndex = currentKeymapIndex + 1;
751 currentKeymapIndex = nextIndex;
754 function previousKeymap() {
755 var prevIndex = keymapCount - 1;
757 if (currentKeymapIndex > 0) {
758 prevIndex = currentKeymapIndex - 1;
760 currentKeymapIndex = prevIndex;
763 function switchToKeymap() {
765 mainAppWindow.switchToKeymap(keymaps[currentKeymapIndex]);
770 onMainAppWindowChanged: keymapPriv.switchToKeymap()
773 id: shutdownFadeOutRectangle
780 NumberAnimation on opacity {
785 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
786 DBusUnitySessionService.shutdown();