2 * Copyright (C) 2013 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.0
19 import AccountsService 0.1
21 import Unity.Application 0.1
22 import Ubuntu.Components 0.1
23 import Ubuntu.Components.Popups 1.0
24 import Ubuntu.Gestures 0.1
25 import Ubuntu.Telephony 0.1 as Telephony
26 import Unity.Connectivity 0.1
27 import Unity.Launcher 0.1
29 import LightDM 0.1 as LightDM
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
48 // Disable everything while greeter is waiting, so that the user can't swipe
49 // the greeter or launcher until we know whether the session is locked.
50 enabled: !greeter.waiting
52 // this is only here to select the width / height of the window if not running fullscreen
53 property bool tablet: false
54 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
55 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
57 property real edgeSize: units.gu(2)
58 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
59 property url background: asImageTester.status == Image.Ready ? asImageTester.source
60 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61 readonly property real panelHeight: panel.panelHeight
63 property bool sideStageEnabled: shell.width >= units.gu(100)
64 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
66 property int orientation
67 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
68 onDeviceOrientationAngleChanged: {
69 if (!OrientationLock.enabled) {
70 orientation = Screen.orientation;
73 readonly property bool orientationLockEnabled: OrientationLock.enabled
74 onOrientationLockEnabledChanged: {
75 if (orientationLockEnabled) {
76 OrientationLock.savedOrientation = Screen.orientation;
78 orientation = Screen.orientation;
82 function activateApplication(appId) {
83 if (ApplicationManager.findApplication(appId)) {
84 ApplicationManager.requestFocusApplication(appId);
86 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
87 ApplicationManager.startApplication(appId, execFlags);
91 function startLockedApp(app) {
93 greeter.lockedApp = app;
95 shell.activateApplication(app);
98 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
101 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
109 id: backgroundSettings
110 objectName: "backgroundSettings"
111 schema.id: "org.gnome.desktop.background"
114 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
117 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
125 id: usageModeSettings
126 schema.id: "com.canonical.Unity8"
130 target: LauncherModel
131 property: "applicationManager"
132 value: ApplicationManager
135 Component.onCompleted: {
136 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
137 if (ApplicationManager.count > 0) {
138 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
140 if (orientationLockEnabled) {
141 orientation = OrientationLock.savedOrientation;
151 objectName: "dashCommunicator"
155 id: physicalKeysMapper
157 onPowerKeyLongPressed: dialogs.showPowerDialog()
158 onVolumeDownTriggered: volumeControl.volumeDown();
159 onVolumeUpTriggered: volumeControl.volumeUp();
160 onScreenshotTriggered: screenGrabber.capture();
166 enabled: Powerd.status === Powerd.On
170 target: ApplicationManager
171 property: "forceDashActive"
172 value: launcher.shown || launcher.dashSwipe
176 Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
177 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
184 height: parent.height
185 visible: !ApplicationManager.empty
188 target: ApplicationManager
190 // This signal is also fired when we try to focus the current app
191 // again. We rely on this!
192 onFocusedApplicationIdChanged: {
193 var appId = ApplicationManager.focusedApplicationId;
195 if (tutorial.running && appId != "" && appId != "unity8-dash") {
196 // If this happens on first boot, we may be in edge
197 // tutorial or wizard while receiving a call. But a call
198 // is more important than wizard so just bail out of those.
203 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
204 // If we are in the middle of a call, make dialer lockedApp and show it.
205 // This can happen if user backs out of dialer back to greeter, then
206 // launches dialer again.
207 greeter.lockedApp = appId;
209 greeter.notifyAppFocused(appId);
211 panel.indicators.hide();
214 onApplicationAdded: {
220 id: applicationsDisplayLoader
221 objectName: "applicationsDisplayLoader"
224 // When we have a locked app, we only want to show that one app.
225 // FIXME: do this in a less traumatic way. We currently only allow
226 // locked apps in phone mode (see FIXME in Lockscreen component in
227 // this same file). When that changes, we need to do something
228 // nicer here. But this code is currently just to prevent a
229 // theoretical attack where user enters lockedApp mode, then makes
230 // the screen larger (maybe connects to monitor) and tries to enter
232 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
233 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
234 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
236 property bool interactive: tutorial.spreadEnabled
238 && panel.indicators.fullyClosed
239 && launcher.progress == 0
240 && !notifications.useModal
242 onInteractiveChanged: { if (interactive) { focus = true; } }
245 target: applicationsDisplayLoader.item
246 property: "objectName"
250 target: applicationsDisplayLoader.item
251 property: "dragAreaWidth"
252 value: shell.edgeSize
255 target: applicationsDisplayLoader.item
256 property: "maximizedAppTopMargin"
257 // Not just using panel.panelHeight as that changes depending on the focused app.
258 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
261 target: applicationsDisplayLoader.item
262 property: "interactive"
263 value: applicationsDisplayLoader.interactive
266 target: applicationsDisplayLoader.item
267 property: "spreadEnabled"
268 value: tutorial.spreadEnabled && !greeter.hasLockedApp
271 target: applicationsDisplayLoader.item
272 property: "inverseProgress"
273 value: greeter.locked ? 0 : launcher.progress
276 target: applicationsDisplayLoader.item
277 property: "orientation"
278 value: shell.orientation
281 target: applicationsDisplayLoader.item
282 property: "background"
283 value: shell.background
290 objectName: "inputMethod"
291 anchors { fill: parent; topMargin: panel.panelHeight }
292 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
296 target: SurfaceManager
298 if (surface.type == MirSurfaceItem.InputMethod) {
299 inputMethod.surface = surface;
303 onSurfaceDestroyed: {
304 if (inputMethod.surface == surface) {
305 inputMethod.surface = null;
306 surface.parent = null;
308 if (!surface.parent) {
309 // there's no one displaying it. delete it right away
315 target: SessionManager
317 if (!session.parentSession && !session.application) {
318 // nothing is using it. delete it right away
326 objectName: "greeter"
328 hides: [launcher, panel.indicators]
329 tabletMode: shell.sideStageEnabled
330 launcherOffset: launcher.progress
331 forcedUnlock: tutorial.running
332 background: shell.background
335 anchors.topMargin: panel.panelHeight
337 // avoid overlapping with Launcher's edge drag area
338 // FIXME: Fix TouchRegistry & friends and remove this workaround
339 // Issue involves launcher's DDA getting disabled on a long
341 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
348 if (!tutorial.running) {
353 onEmergencyCall: startLockedApp("dialer-app")
356 // See powerConnection for why this is useful
357 id: showGreeterDelayed
365 target: ApplicationManager
366 property: "suspended"
376 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
377 // We just received an incoming call while locked. The
378 // indicator will have already launched dialer-app for us, but
379 // there is a race between "hasCalls" changing and the dialer
380 // starting up. So in case we lose that race, we'll start/
381 // focus the dialer ourselves here too. Even if the indicator
382 // didn't launch the dialer for some reason (or maybe a call
383 // started via some other means), if an active call is
384 // happening, we want to be in the dialer.
385 startLockedApp("dialer-app")
395 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
396 !callManager.hasCalls && !tutorial.running) {
397 // We don't want to simply call greeter.showNow() here, because
398 // that will take too long. Qt will delay button event
399 // handling until the greeter is done loading and may think the
400 // user held down the power button the whole time, leading to a
401 // power dialog being shown. Instead, delay showing the
402 // greeter until we've finished handling the event. We could
403 // make the greeter load asynchronously instead, but that
404 // introduces a whole host of timing issues, especially with
405 // its animations. So this is simpler.
406 showGreeterDelayed.start();
411 function showHome() {
412 if (tutorial.running) {
416 greeter.notifyAboutToFocusApp("unity8-dash");
418 var animate = !LightDM.Greeter.active && !stages.shown
419 dash.setCurrentScope(0, animate, false)
420 ApplicationManager.requestFocusApplication("unity8-dash")
423 function showDash() {
424 if (greeter.notifyShowingDashFromDrag()) {
428 if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") {
429 ApplicationManager.requestFocusApplication("unity8-dash")
443 anchors.fill: parent //because this draws indicator menus
446 available: tutorial.panelEnabled
447 && (!greeter.locked || AccountsService.enableIndicatorsWhileLocked)
448 && !greeter.hasLockedApp
449 contentEnabled: tutorial.panelContentEnabled
450 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
452 minimizedPanelHeight: units.gu(3)
453 expandedPanelHeight: units.gu(7)
455 indicatorsModel: Indicators.IndicatorsModel {
456 // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
457 profile: indicatorProfile
458 Component.onCompleted: load()
462 greeterShown: greeter.shown
465 property bool topmostApplicationIsFullscreen:
466 ApplicationManager.focusedApplicationId &&
467 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
469 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
470 || greeter.hasLockedApp
475 objectName: "launcher"
477 readonly property bool dashSwipe: progress > 0
479 anchors.top: parent.top
480 anchors.topMargin: inverted ? 0 : panel.panelHeight
481 anchors.bottom: parent.bottom
483 dragAreaWidth: shell.edgeSize
484 available: tutorial.launcherEnabled
485 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
486 && !greeter.hasLockedApp
487 inverted: usageModeSettings.usageMode === "Staged"
488 shadeBackground: !tutorial.running
490 onShowDashHome: showHome()
492 onDashSwipeChanged: {
494 dash.setCurrentScope(0, false, true)
497 onLauncherApplicationSelected: {
498 if (!tutorial.running) {
499 greeter.notifyAboutToFocusApp(appId);
500 shell.activateApplication(appId)
505 panel.indicators.hide()
514 background: shell.background
516 function unlockWhenDoneWithWizard() {
518 Connectivity.unlockAllModems();
522 Component.onCompleted: unlockWhenDoneWithWizard()
523 onActiveChanged: unlockWhenDoneWithWizard()
527 id: modalNotificationBackground
529 visible: notifications.useModal && (notifications.state == "narrow")
542 model: NotificationBackend.Model
545 y: topmostIsFullscreen ? 0 : panel.panelHeight
546 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
551 when: overlay.width <= units.gu(60)
553 target: notifications
554 anchors.left: parent.left
555 anchors.right: parent.right
560 when: overlay.width > units.gu(60)
562 target: notifications
563 anchors.left: undefined
564 anchors.right: parent.right
566 PropertyChanges { target: notifications; width: units.gu(38) }
577 shutdownFadeOutRectangle.enabled = true;
578 shutdownFadeOutRectangle.visible = true;
579 shutdownFadeOut.start();
585 objectName: "tutorial"
586 active: AccountsService.demoEdges
587 paused: LightDM.Greeter.active
592 edgeSize: shell.edgeSize
595 AccountsService.demoEdges = false;
596 active = false; // for immediate response / if AS is having problems
601 target: SessionBroadcast
602 onShowHome: showHome()
606 id: shutdownFadeOutRectangle
607 z: screenGrabber.z + 10
613 NumberAnimation on opacity {
618 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
619 DBusUnitySessionService.Shutdown();