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