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.Launcher 0.1
28 import LightDM 0.1 as LightDM
30 import SessionBroadcast 0.1
35 import "Notifications"
39 import Unity.Notifications 1.0 as NotificationBackend
40 import Unity.Session 0.1
41 import Unity.DashCommunicator 0.1
42 import Unity.Indicators 0.1 as Indicators
47 // Disable everything while greeter is waiting, so that the user can't swipe
48 // the greeter or launcher until we know whether the session is locked.
49 enabled: !greeter.waiting
51 // this is only here to select the width / height of the window if not running fullscreen
52 property bool tablet: false
53 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
54 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
56 property real edgeSize: units.gu(2)
57 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
58 property url background: asImageTester.status == Image.Ready ? asImageTester.source
59 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
60 readonly property real panelHeight: panel.panelHeight
62 property bool sideStageEnabled: shell.width >= units.gu(100)
63 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
65 property int orientation
66 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
67 onDeviceOrientationAngleChanged: {
68 if (!OrientationLock.enabled) {
69 orientation = Screen.orientation;
72 readonly property bool orientationLockEnabled: OrientationLock.enabled
73 onOrientationLockEnabledChanged: {
74 if (orientationLockEnabled) {
75 OrientationLock.savedOrientation = Screen.orientation;
77 orientation = Screen.orientation;
81 function activateApplication(appId) {
82 if (ApplicationManager.findApplication(appId)) {
83 ApplicationManager.requestFocusApplication(appId);
85 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
86 ApplicationManager.startApplication(appId, execFlags);
90 function startLockedApp(app) {
92 greeter.lockedApp = app;
94 shell.activateApplication(app);
97 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
100 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
108 id: backgroundSettings
109 objectName: "backgroundSettings"
110 schema.id: "org.gnome.desktop.background"
113 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
116 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
124 id: usageModeSettings
125 schema.id: "com.canonical.Unity8"
129 target: LauncherModel
130 property: "applicationManager"
131 value: ApplicationManager
134 Component.onCompleted: {
135 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
136 if (ApplicationManager.count > 0) {
137 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
139 if (orientationLockEnabled) {
140 orientation = OrientationLock.savedOrientation;
150 objectName: "dashCommunicator"
154 id: physicalKeysMapper
156 onPowerKeyLongPressed: dialogs.showPowerDialog()
157 onVolumeDownTriggered: volumeControl.volumeDown();
158 onVolumeUpTriggered: volumeControl.volumeUp();
159 onScreenshotTriggered: screenGrabber.capture();
165 enabled: Powerd.status === Powerd.On
169 target: ApplicationManager
170 property: "forceDashActive"
171 value: launcher.shown || launcher.dashSwipe
175 Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
176 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
183 height: parent.height
184 visible: !ApplicationManager.empty
187 target: ApplicationManager
189 // This signal is also fired when we try to focus the current app
190 // again. We rely on this!
191 onFocusedApplicationIdChanged: {
192 var appId = ApplicationManager.focusedApplicationId;
194 if (tutorial.running && appId != "unity8-dash") {
195 // If this happens on first boot, we may be in edge
196 // tutorial or wizard while receiving a call. But a call
197 // is more important than wizard so just bail out of those.
202 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
203 // If we are in the middle of a call, make dialer lockedApp and show it.
204 // This can happen if user backs out of dialer back to greeter, then
205 // launches dialer again.
206 greeter.lockedApp = appId;
208 greeter.notifyAppFocused(appId);
210 panel.indicators.hide();
213 onApplicationAdded: {
219 id: applicationsDisplayLoader
220 objectName: "applicationsDisplayLoader"
223 // When we have a locked app, we only want to show that one app.
224 // FIXME: do this in a less traumatic way. We currently only allow
225 // locked apps in phone mode (see FIXME in Lockscreen component in
226 // this same file). When that changes, we need to do something
227 // nicer here. But this code is currently just to prevent a
228 // theoretical attack where user enters lockedApp mode, then makes
229 // the screen larger (maybe connects to monitor) and tries to enter
231 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
232 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
233 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
235 property bool interactive: tutorial.spreadEnabled
237 && panel.indicators.fullyClosed
238 && launcher.progress == 0
239 && !notifications.useModal
241 onInteractiveChanged: { if (interactive) { focus = true; } }
244 target: applicationsDisplayLoader.item
245 property: "objectName"
249 target: applicationsDisplayLoader.item
250 property: "dragAreaWidth"
251 value: shell.edgeSize
254 target: applicationsDisplayLoader.item
255 property: "maximizedAppTopMargin"
256 // Not just using panel.panelHeight as that changes depending on the focused app.
257 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
260 target: applicationsDisplayLoader.item
261 property: "interactive"
262 value: applicationsDisplayLoader.interactive
265 target: applicationsDisplayLoader.item
266 property: "spreadEnabled"
267 value: tutorial.spreadEnabled && !greeter.hasLockedApp
270 target: applicationsDisplayLoader.item
271 property: "inverseProgress"
272 value: greeter.locked ? 0 : launcher.progress
275 target: applicationsDisplayLoader.item
276 property: "orientation"
277 value: shell.orientation
280 target: applicationsDisplayLoader.item
281 property: "background"
282 value: shell.background
289 objectName: "inputMethod"
290 anchors { fill: parent; topMargin: panel.panelHeight }
291 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
295 target: SurfaceManager
297 if (surface.type == MirSurfaceItem.InputMethod) {
298 inputMethod.surface = surface;
302 onSurfaceDestroyed: {
303 if (inputMethod.surface == surface) {
304 inputMethod.surface = null;
305 surface.parent = null;
307 if (!surface.parent) {
308 // there's no one displaying it. delete it right away
314 target: SessionManager
316 if (!session.parentSession && !session.application) {
317 // nothing is using it. delete it right away
325 objectName: "greeter"
327 hides: [launcher, panel.indicators]
328 tabletMode: shell.sideStageEnabled
329 launcherOffset: launcher.progress
330 forcedUnlock: tutorial.running
331 background: shell.background
334 anchors.topMargin: panel.panelHeight
336 // avoid overlapping with Launcher's edge drag area
337 // FIXME: Fix TouchRegistry & friends and remove this workaround
338 // Issue involves launcher's DDA getting disabled on a long
340 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
347 if (!tutorial.running) {
352 onEmergencyCall: startLockedApp("dialer-app")
355 // See powerConnection for why this is useful
356 id: showGreeterDelayed
364 target: ApplicationManager
365 property: "suspended"
375 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
376 // We just received an incoming call while locked. The
377 // indicator will have already launched dialer-app for us, but
378 // there is a race between "hasCalls" changing and the dialer
379 // starting up. So in case we lose that race, we'll start/
380 // focus the dialer ourselves here too. Even if the indicator
381 // didn't launch the dialer for some reason (or maybe a call
382 // started via some other means), if an active call is
383 // happening, we want to be in the dialer.
384 startLockedApp("dialer-app")
394 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
395 !callManager.hasCalls && !tutorial.running) {
396 // We don't want to simply call greeter.showNow() here, because
397 // that will take too long. Qt will delay button event
398 // handling until the greeter is done loading and may think the
399 // user held down the power button the whole time, leading to a
400 // power dialog being shown. Instead, delay showing the
401 // greeter until we've finished handling the event. We could
402 // make the greeter load asynchronously instead, but that
403 // introduces a whole host of timing issues, especially with
404 // its animations. So this is simpler.
405 showGreeterDelayed.start();
410 function showHome() {
411 if (tutorial.running) {
415 greeter.notifyAboutToFocusApp("unity8-dash");
417 var animate = !LightDM.Greeter.active && !stages.shown
418 dash.setCurrentScope(0, animate, false)
419 ApplicationManager.requestFocusApplication("unity8-dash")
422 function showDash() {
423 if (greeter.notifyShowingDashFromDrag()) {
427 if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") {
428 ApplicationManager.requestFocusApplication("unity8-dash")
442 anchors.fill: parent //because this draws indicator menus
445 available: tutorial.panelEnabled
446 && (!greeter.locked || AccountsService.enableIndicatorsWhileLocked)
447 && !greeter.hasLockedApp
448 contentEnabled: tutorial.panelContentEnabled
449 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
451 minimizedPanelHeight: units.gu(3)
452 expandedPanelHeight: units.gu(7)
454 indicatorsModel: Indicators.IndicatorsModel {
455 // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
456 profile: indicatorProfile
457 Component.onCompleted: load()
461 greeterShown: greeter.shown
464 property bool topmostApplicationIsFullscreen:
465 ApplicationManager.focusedApplicationId &&
466 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
468 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
469 || greeter.hasLockedApp
474 objectName: "launcher"
476 readonly property bool dashSwipe: progress > 0
478 anchors.top: parent.top
479 anchors.topMargin: inverted ? 0 : panel.panelHeight
480 anchors.bottom: parent.bottom
482 dragAreaWidth: shell.edgeSize
483 available: tutorial.launcherEnabled
484 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
485 && !greeter.hasLockedApp
486 inverted: usageModeSettings.usageMode === "Staged"
487 shadeBackground: !tutorial.running
489 onShowDashHome: showHome()
491 onDashSwipeChanged: {
493 dash.setCurrentScope(0, false, true)
496 onLauncherApplicationSelected: {
497 if (!tutorial.running) {
498 greeter.notifyAboutToFocusApp(appId);
499 shell.activateApplication(appId)
504 panel.indicators.hide()
512 background: shell.background
516 id: modalNotificationBackground
518 visible: notifications.useModal && (notifications.state == "narrow")
531 model: NotificationBackend.Model
534 y: topmostIsFullscreen ? 0 : panel.panelHeight
535 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
540 when: overlay.width <= units.gu(60)
542 target: notifications
543 anchors.left: parent.left
544 anchors.right: parent.right
549 when: overlay.width > units.gu(60)
551 target: notifications
552 anchors.left: undefined
553 anchors.right: parent.right
555 PropertyChanges { target: notifications; width: units.gu(38) }
566 shutdownFadeOutRectangle.enabled = true;
567 shutdownFadeOutRectangle.visible = true;
568 shutdownFadeOut.start();
574 objectName: "tutorial"
575 active: AccountsService.demoEdges
576 paused: LightDM.Greeter.active
581 edgeSize: shell.edgeSize
584 AccountsService.demoEdges = false;
585 active = false; // for immediate response / if AS is having problems
590 target: SessionBroadcast
591 onShowHome: showHome()
595 id: shutdownFadeOutRectangle
596 z: screenGrabber.z + 10
602 NumberAnimation on opacity {
607 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
608 DBusUnitySessionService.Shutdown();