on 6d66001 help sign in
unity8/qml/Shell.qml
591 lines 18 kB history raw
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
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.
7 *
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.
12 *
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/>.
15 */
17import QtQuick 2.0
18import AccountsService 0.1
19import GSettings 1.0
20import Unity.Application 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Gestures 0.1
23import Ubuntu.Telephony 0.1 as Telephony
24import Unity.Launcher 0.1
25import Utils 0.1
26import LightDM 0.1 as LightDM
27import Powerd 0.1
28import SessionBroadcast 0.1
29import "Greeter"
30import "Launcher"
31import "Panel"
32import "Components"
33import "Notifications"
34import "Stages"
35import Unity.Notifications 1.0 as NotificationBackend
36import Unity.Session 0.1
37import Unity.DashCommunicator 0.1
39Item {
40 id: shell
42 // this is only here to select the width / height of the window if not running fullscreen
43 property bool tablet: false
44 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
45 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
47 property real edgeSize: units.gu(2)
48 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
49 property url background
50 readonly property real panelHeight: panel.panelHeight
52 property bool sideStageEnabled: shell.width >= units.gu(100)
53 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
55 function activateApplication(appId) {
56 if (ApplicationManager.findApplication(appId)) {
57 ApplicationManager.requestFocusApplication(appId);
58 } else {
59 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
60 ApplicationManager.startApplication(appId, execFlags);
61 }
62 }
64 Binding {
65 target: LauncherModel
66 property: "applicationManager"
67 value: ApplicationManager
68 }
70 Component.onCompleted: {
71 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
72 }
74 GSettings {
75 id: backgroundSettings
76 schema.id: "org.gnome.desktop.background"
77 }
78 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
79 onGSettingsPictureChanged: {
80 shell.background = gSettingsPicture
81 }
83 VolumeControl {
84 id: volumeControl
85 }
87 DashCommunicator {
88 id: dash
89 objectName: "dashCommunicator"
90 }
92 WindowKeysFilter {
93 // Handle but do not filter out volume keys
94 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
95 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
97 Keys.onPressed: {
98 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
99 dialogs.onPowerKeyPressed();
100 event.accepted = true;
101 } else {
102 event.accepted = false;
103 }
104 }
106 Keys.onReleased: {
107 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
108 dialogs.onPowerKeyReleased();
109 event.accepted = true;
110 } else {
111 event.accepted = false;
112 }
113 }
114 }
116 Item {
117 id: stages
118 objectName: "stages"
119 width: parent.width
120 height: parent.height
121 visible: !ApplicationManager.empty
123 Connections {
124 target: ApplicationManager
125 onFocusRequested: {
126 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
127 lockscreen.show();
128 }
129 greeter.hide();
130 }
132 onFocusedApplicationIdChanged: {
133 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
134 lockscreen.show();
135 }
136 panel.indicators.hide();
137 }
139 onApplicationAdded: {
140 if (greeter.shown && appId != "unity8-dash") {
141 greeter.hide();
142 }
143 }
144 }
146 Loader {
147 id: applicationsDisplayLoader
148 anchors.fill: parent
150 source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
152 Binding {
153 target: applicationsDisplayLoader.item
154 property: "objectName"
155 value: "stage"
156 }
157 Binding {
158 target: applicationsDisplayLoader.item
159 property: "dragAreaWidth"
160 value: shell.edgeSize
161 }
162 Binding {
163 target: applicationsDisplayLoader.item
164 property: "maximizedAppTopMargin"
165 // Not just using panel.panelHeight as that changes depending on the focused app.
166 value: panel.indicators.panelHeight
167 }
168 Binding {
169 target: applicationsDisplayLoader.item
170 property: "interactive"
171 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
172 }
173 Binding {
174 target: applicationsDisplayLoader.item
175 property: "spreadEnabled"
176 value: edgeDemo.stagesEnabled && greeter.fakeActiveForApp === "" // to support emergency dialer hack
177 }
178 Binding {
179 target: applicationsDisplayLoader.item
180 property: "inverseProgress"
181 value: launcher.progress
182 }
183 }
184 }
186 InputMethod {
187 id: inputMethod
188 objectName: "inputMethod"
189 anchors { fill: parent; topMargin: panel.panelHeight }
190 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
191 }
193 Connections {
194 target: SurfaceManager
195 onSurfaceCreated: {
196 if (surface.type == MirSurfaceItem.InputMethod) {
197 inputMethod.surface = surface;
198 }
199 }
201 onSurfaceDestroyed: {
202 if (inputMethod.surface == surface) {
203 inputMethod.surface = null;
204 surface.parent = null;
205 }
206 if (!surface.parent) {
207 // there's no one displaying it. delete it right away
208 surface.release();
209 }
210 }
211 }
213 Lockscreen {
214 id: lockscreen
215 objectName: "lockscreen"
217 readonly property int backgroundTopMargin: -panel.panelHeight
219 hides: [launcher, panel.indicators]
220 shown: false
221 enabled: true
222 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
223 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
224 y: panel.panelHeight
225 visible: required
226 width: parent.width
227 height: parent.height - panel.panelHeight
228 background: shell.background
229 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
230 minPinLength: 4
231 maxPinLength: 4
233 onEntered: LightDM.Greeter.respond(passphrase);
234 onCancel: greeter.show()
235 onEmergencyCall: {
236 greeter.fakeActiveForApp = "dialer-app"
237 shell.activateApplication("dialer-app")
238 lockscreen.hide()
239 }
241 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
243 Component.onCompleted: {
244 if (greeter.narrowMode) {
245 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
246 }
247 }
248 }
250 Connections {
251 target: LightDM.Greeter
253 onShowGreeter: greeter.show()
255 onShowPrompt: {
256 if (greeter.narrowMode) {
257 lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text.toLowerCase());
258 lockscreen.show();
259 }
260 }
262 onPromptlessChanged: {
263 if (LightDM.Greeter.promptless) {
264 lockscreen.hide()
265 } else {
266 lockscreen.reset();
267 lockscreen.show();
268 }
269 }
271 onAuthenticationComplete: {
272 if (LightDM.Greeter.promptless) {
273 return;
274 }
275 if (LightDM.Greeter.authenticated) {
276 lockscreen.hide();
277 greeter.login();
278 } else {
279 lockscreen.clear(true);
280 if (greeter.narrowMode) {
281 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
282 }
283 }
284 }
285 }
287 Binding {
288 target: LightDM.Greeter
289 property: "active"
290 value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
291 }
293 Rectangle {
294 anchors.fill: parent
295 color: "black"
296 opacity: greeterWrapper.showProgress * 0.8
297 }
299 Item {
300 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
301 id: greeterWrapper
302 x: launcher.progress
303 y: panel.panelHeight
304 width: parent.width
305 height: parent.height - panel.panelHeight
307 Behavior on x {
308 enabled: !launcher.dashSwipe
309 StandardAnimation {}
310 }
312 property bool fullyShown: showProgress === 1.0
313 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
314 onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
316 Greeter {
317 id: greeter
318 objectName: "greeter"
320 signal sessionStarted() // helpful for tests
322 property string fakeActiveForApp: ""
324 available: true
325 hides: [launcher, panel.indicators]
326 shown: true
328 defaultBackground: shell.background
330 width: parent.width
331 height: parent.height
333 dragHandleWidth: shell.edgeSize
335 function login() {
336 enabled = false;
337 if (LightDM.Greeter.startSessionSync()) {
338 sessionStarted();
339 greeter.hide();
340 lockscreen.hide();
341 launcher.hide();
342 }
343 enabled = true;
344 }
346 onShownChanged: {
347 if (shown) {
348 if (greeter.narrowMode) {
349 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
350 }
351 if (!LightDM.Greeter.promptless) {
352 lockscreen.reset();
353 lockscreen.show();
354 }
355 greeter.fakeActiveForApp = "";
356 greeter.forceActiveFocus();
357 }
358 }
360 onUnlocked: greeter.hide()
361 onSelected: {
362 // Update launcher items for new user
363 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
364 AccountsService.user = user;
365 LauncherModel.setUser(user);
366 }
368 onTease: launcher.tease()
370 Binding {
371 target: ApplicationManager
372 property: "suspended"
373 value: greeter.shown && greeterWrapper.showProgress == 1
374 }
375 }
376 }
378 Connections {
379 id: powerConnection
380 target: Powerd
382 onStatusChanged: {
383 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
384 greeter.showNow()
385 }
387 // No reason to chew demo CPU when user isn't watching
388 if (Powerd.status === Powerd.Off) {
389 edgeDemo.paused = true;
390 } else if (Powerd.status === Powerd.On) {
391 edgeDemo.paused = false;
392 }
393 }
394 }
396 function showHome() {
397 if (edgeDemo.running) {
398 return
399 }
401 if (LightDM.Greeter.active) {
402 if (!LightDM.Greeter.promptless) {
403 lockscreen.show()
404 }
405 greeter.hide()
406 }
408 var animate = !LightDM.Greeter.active && !stages.shown
409 dash.setCurrentScope("clickscope", animate, false)
410 ApplicationManager.requestFocusApplication("unity8-dash")
411 }
413 function showDash() {
414 if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
415 return;
416 }
417 if (greeter.shown) {
418 greeter.hideRight();
419 launcher.fadeOut();
420 }
422 ApplicationManager.requestFocusApplication("unity8-dash")
423 launcher.fadeOut();
424 }
426 Item {
427 id: overlay
428 z: 10
430 anchors.fill: parent
432 Panel {
433 id: panel
434 objectName: "panel"
435 anchors.fill: parent //because this draws indicator menus
436 indicators {
437 hides: [launcher]
438 available: edgeDemo.panelEnabled && greeter.fakeActiveForApp === ""
439 contentEnabled: edgeDemo.panelContentEnabled
440 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
441 panelHeight: units.gu(3)
442 }
444 property bool topmostApplicationIsFullscreen:
445 ApplicationManager.focusedApplicationId &&
446 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
448 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
449 || greeter.fakeActiveForApp !== ""
450 }
452 Launcher {
453 id: launcher
454 objectName: "launcher"
456 readonly property bool dashSwipe: progress > 0
458 anchors.top: parent.top
459 anchors.bottom: parent.bottom
460 width: parent.width
461 dragAreaWidth: shell.edgeSize
462 available: edgeDemo.launcherEnabled && greeter.fakeActiveForApp === ""
464 onShowDashHome: showHome()
465 onDash: showDash()
466 onDashSwipeChanged: {
467 if (dashSwipe && ApplicationManager.focusedApplicationId !== "unity8-dash") {
468 dash.setCurrentScope("clickscope", false, true)
469 }
470 }
471 onLauncherApplicationSelected: {
472 if (greeter.fakeActiveForApp !== "") {
473 lockscreen.show()
474 }
475 if (!edgeDemo.running)
476 shell.activateApplication(appId)
477 }
478 onShownChanged: {
479 if (shown) {
480 panel.indicators.hide()
481 }
482 }
483 }
485 Rectangle {
486 id: modalNotificationBackground
488 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
489 color: "#000000"
490 anchors.fill: parent
491 opacity: 0.5
493 MouseArea {
494 anchors.fill: parent
495 }
496 }
498 Notifications {
499 id: notifications
501 model: NotificationBackend.Model
502 margin: units.gu(1)
504 y: panel.panelHeight
505 width: parent.width
506 height: parent.height - panel.panelHeight
508 states: [
509 State {
510 name: "narrow"
511 when: overlay.width <= units.gu(60)
512 AnchorChanges { target: notifications; anchors.left: parent.left }
513 },
514 State {
515 name: "wide"
516 when: overlay.width > units.gu(60)
517 AnchorChanges { target: notifications; anchors.left: undefined }
518 PropertyChanges { target: notifications; width: units.gu(38) }
519 }
520 ]
521 }
522 }
524 Binding {
525 target: i18n
526 property: "domain"
527 value: "unity8"
528 }
530 Dialogs {
531 id: dialogs
532 anchors.fill: parent
533 z: overlay.z + 10
534 onPowerOffClicked: {
535 shutdownFadeOutRectangle.enabled = true;
536 shutdownFadeOutRectangle.visible = true;
537 shutdownFadeOut.start();
538 }
539 }
541 Label {
542 id: alphaDisclaimerLabel
543 anchors.centerIn: parent
544 visible: ApplicationManager.fake ? ApplicationManager.fake : false
545 z: dialogs.z + 10
546 text: "EARLY ALPHA\nNOT READY FOR USE"
547 color: "lightgrey"
548 opacity: 0.2
549 font.weight: Font.Black
550 horizontalAlignment: Text.AlignHCenter
551 verticalAlignment: Text.AlignVCenter
552 fontSizeMode: Text.Fit
553 rotation: -45
554 scale: Math.min(parent.width, parent.height) / width
555 }
557 EdgeDemo {
558 id: edgeDemo
559 z: alphaDisclaimerLabel.z + 10
560 greeter: greeter
561 launcher: launcher
562 indicators: panel.indicators
563 stages: stages
564 }
566 Connections {
567 target: SessionBroadcast
568 onShowHome: showHome()
569 }
571 Rectangle {
572 id: shutdownFadeOutRectangle
573 z: edgeDemo.z + 10
574 enabled: false
575 visible: false
576 color: "black"
577 anchors.fill: parent
578 opacity: 0.0
579 NumberAnimation on opacity {
580 id: shutdownFadeOut
581 from: 0.0
582 to: 1.0
583 onStopped: {
584 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
585 DBusUnitySessionService.Shutdown();
586 }
587 }
588 }
589 }