on e9630e0 help sign in
unity8/Shell.qml
621 lines 20 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 LightDM 0.1 as LightDM
25import Powerd 0.1
26import SessionBroadcast 0.1
27import "Dash"
28import "Greeter"
29import "Launcher"
30import "Panel"
31import "Hud"
32import "Components"
33import "Bottombar"
34import "SideStage"
35import "Notifications"
36import Unity.Notifications 1.0 as NotificationBackend
38FocusScope {
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: 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 dashShown: dash.shown
53 function activateApplication(appId) {
54 if (ApplicationManager.findApplication(appId)) {
55 print("Shell.qml: activating app", appId);
56 ApplicationManager.activateApplication(appId);
57 } else {
58 print("starting app", appId);
60 // FIXME:
61// var execFlags = sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
62 var execFlags = ApplicationManager.ForceMainStage;
64 ApplicationManager.startApplication(appId, execFlags);
65 }
66 }
68 Binding {
69 target: LauncherModel
70 property: "applicationManager"
71 value: ApplicationManager
72 }
74 Component.onCompleted: {
75 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
76 }
78 GSettings {
79 id: backgroundSettings
80 schema.id: "org.gnome.desktop.background"
81 }
82 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
83 onGSettingsPictureChanged: {
84 shell.background = gSettingsPicture
85 }
87 // This is a dummy image that is needed to determine if the picture url
88 // in backgroundSettings points to a valid picture file.
89 // We can't do this with the real background image because setting a
90 // new source in onStatusChanged triggers a binding loop detection
91 // inside Image, which causes it not to render even though a valid source
92 // would be set. We don't mind about this image staying black and just
93 // use it for verification to populate the source for the real
94 // background image.
95 Image {
96 source: shell.background
97 height: 0
98 width: 0
99 sourceSize.height: 0
100 sourceSize.width: 0
101 onStatusChanged: {
102 if (status == Image.Error && source != shell.defaultBackground) {
103 shell.background = defaultBackground
104 }
105 }
106 }
108 VolumeControl {
109 id: volumeControl
110 }
112 Keys.onVolumeUpPressed: volumeControl.volumeUp()
113 Keys.onVolumeDownPressed: volumeControl.volumeDown()
115 Item {
116 id: underlay
117 objectName: "underlay"
118 anchors.fill: parent
120 // Whether the underlay is fully covered by opaque UI elements.
121 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
123 readonly property bool applicationRunning: ApplicationManager.focusedApplicationId.length > 0
125 // Whether the user should see the topmost application surface (if there's one at all).
126 readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.painting
128 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
129 // through the translucent parts of the shell surface.
130 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
132 onVisibleChanged: print("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", visible, fullyCovered, applicationSurfaceShouldBeSeen,
133 applicationRunning, stages.fullyHidden, stages.usingScreenshots)
135 CrossFadeImage {
136 id: backgroundImage
137 objectName: "backgroundImage"
139 anchors.fill: parent
140 source: shell.background
141 fillMode: Image.PreserveAspectCrop
142 }
144 Rectangle {
145 anchors.fill: parent
146 color: "black"
147 opacity: dash.disappearingAnimationProgress
148 }
150 Dash {
151 id: dash
152 objectName: "dash"
154 available: !greeter.shown && !lockscreen.shown
155 hides: [stages, launcher, panel.indicators]
156 shown: disappearingAnimationProgress !== 1.0
157 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
159 anchors {
160 fill: parent
161 topMargin: panel.panelHeight
162 }
164 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
165 opacity: 1.0 - disappearingAnimationProgress
166 property real disappearingAnimationProgress: {
167 if (greeter.shown) {
168 return greeter.showProgress;
169 } else {
170 return stagesOuterContainer.showProgress;
171 }
172 }
174 // FIXME: only necessary because stagesOuterContainer.showProgress and
175 // greeterRevealer.animatedProgress are not animated
176 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
177 }
178 }
180 Item {
181 id: stagesOuterContainer
183 width: parent.width
184 height: parent.height
185 x: launcher.progress
186 Behavior on x {SmoothedAnimation{velocity: 600}}
188 property real showProgress: MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
190 Showable {
191 id: stages
192 objectName: "stages"
194 x: width
196 property bool fullyShown: shown && x == 0 && parent.x == 0
197 property bool fullyHidden: !shown && x == width
199 property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
201 available: !greeter.shown
202 hides: [panel.indicators]
203 shown: false
204 opacity: 1.0
205 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
206 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }
208 width: parent.width
209 height: parent.height
211 onShownChanged: {
212 print("stages shown", shown)
213 if (!shown) {
214 ApplicationManager.unfocusCurrentApplication();
215 }
216 }
218 onFullyShownChanged: {
219 print("kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk", fullyShown)
220 }
222 Connections {
223 target: ApplicationManager
224 onFocusedApplicationIdChanged: {
225 if (ApplicationManager.focusedApplicationId.length > 0) {
226 print("should show stages")
227 stages.show();
228 }
229 }
231 onApplicationAdded: {
232 stages.show();
233 }
234 }
236 Loader {
237 id: applicationsDisplayLoader
238 anchors.fill: parent
240// source: "Stages/StageWithSideStage.qml"
241 source: "Stages/PhoneStage.qml"
243// Rectangle { anchors.fill: parent; color: "blue"; opacity: .5 }
245 Binding {
246 target: applicationsDisplayLoader.item
247 property: "moving"
248 value: !stages.fullyShown
249 }
250 Binding {
251 target: applicationsDisplayLoader.item
252 property: "shown"
253 value: stages.shown
254 }
255 }
256 }
257 }
259 Lockscreen {
260 id: lockscreen
261 objectName: "lockscreen"
263 readonly property int backgroundTopMargin: -panel.panelHeight
265 hides: [launcher, panel.indicators, hud]
266 shown: false
267 enabled: true
268 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
269 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
270 y: panel.panelHeight
271 x: required ? 0 : - width
272 width: parent.width
273 height: parent.height - panel.panelHeight
274 background: shell.background
276 onEntered: LightDM.Greeter.respond(passphrase);
277 onCancel: greeter.show()
279 Component.onCompleted: {
280 if (LightDM.Users.count == 1) {
281 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
282 greeter.selected(0)
283 }
284 }
285 }
287 Connections {
288 target: LightDM.Greeter
290 onShowPrompt: {
291 if (LightDM.Users.count == 1) {
292 // TODO: There's no better way for now to determine if its a PIN or a passphrase.
293 if (text == "PIN") {
294 lockscreen.alphaNumeric = false
295 } else {
296 lockscreen.alphaNumeric = true
297 }
298 lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
299 lockscreen.show();
300 }
301 }
303 onAuthenticationComplete: {
304 if (LightDM.Greeter.promptless) {
305 return;
306 }
307 if (LightDM.Greeter.authenticated) {
308 lockscreen.hide();
309 } else {
310 lockscreen.clear(true);
311 }
312 }
313 }
315 Greeter {
316 id: greeter
317 objectName: "greeter"
319 available: true
320 hides: [launcher, panel.indicators, hud]
321 shown: true
323 defaultBackground: shell.background
325 y: panel.panelHeight
326 width: parent.width
327 height: parent.height - panel.panelHeight
329 dragHandleWidth: shell.edgeSize
331 property var previousMainApp: null
332 property var previousSideApp: null
334 onShownChanged: {
335 if (shown) {
336 lockscreen.reset();
337 // If there is only one user, we start authenticating with that one here.
338 // If there are more users, the Greeter will handle that
339 if (LightDM.Users.count == 1) {
340 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
341 greeter.selected(0);
342 }
343 greeter.forceActiveFocus();
344 }
345 }
347 onUnlocked: greeter.hide()
348 onSelected: {
349 // Update launcher items for new user
350 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
351 AccountsService.user = user;
352 LauncherModel.setUser(user);
353 }
355 onLeftTeaserPressedChanged: {
356 if (leftTeaserPressed) {
357 launcher.tease();
358 }
359 }
360 }
362 InputFilterArea {
363 anchors.fill: parent
364 blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
365 || panel.indicators.shown || hud.shown
366 }
368 Connections {
369 id: powerConnection
370 target: Powerd
372 onDisplayPowerStateChange: {
373 // We ignore any display-off signals when the proximity sensor
374 // is active. This usually indicates something like a phone call.
375 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
376 greeter.showNow();
377 }
379 // No reason to chew demo CPU when user isn't watching
380 if (status == Powerd.Off) {
381 edgeDemo.paused = true;
382 } else if (status == Powerd.On) {
383 edgeDemo.paused = false;
384 }
385 }
386 }
388 function showHome() {
389 var animate = !greeter.shown && !stages.shown
390 greeter.hide()
391 dash.setCurrentScope("home.scope", animate, false)
392 stages.hide()
393 }
395 Item {
396 id: overlay
398 anchors.fill: parent
400 Panel {
401 id: panel
402 anchors.fill: parent //because this draws indicator menus
403 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
404 indicators {
405 hides: [launcher]
406 available: edgeDemo.panelEnabled
407 contentEnabled: edgeDemo.panelContentEnabled
408 }
409 property string focusedAppId: ApplicationManager.focusedApplicationId
410 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
411 fullscreenMode: focusedApplication && focusedApplication.fullscreen
412 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown
414 InputFilterArea {
415 anchors {
416 top: parent.top
417 left: parent.left
418 right: parent.right
419 }
420 height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
421 blockInput: true
422 }
423 }
425 Hud {
426 id: hud
428 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
429 height: parent.height
431 available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && edgeDemo.dashEnabled
432 shown: false
433 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
434 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
436 Connections {
437 target: ApplicationManager
438 onFocusedApplicationIdChanged: hud.hide()
439 }
440 }
442 Revealer {
443 id: hudRevealer
445 enabled: hud.shown
446 width: hud.width
447 anchors.left: hud.left
448 height: parent.height
449 target: hud.revealerTarget
450 closedValue: height
451 openedValue: 0
452 direction: Qt.RightToLeft
453 orientation: Qt.Vertical
454 handleSize: hud.handleHeight
455 onCloseClicked: target.hide()
456 }
458 Bottombar {
459 id: bottombar
460 theHud: hud
461 anchors.fill: parent
462 enabled: hud.available
463 applicationIsOnForeground: ApplicationManager.focusedApplicationId
464 }
466 InputFilterArea {
467 blockInput: launcher.shown
468 anchors {
469 top: parent.top
470 bottom: parent.bottom
471 left: parent.left
472 }
473 width: launcher.width
474 }
476 Launcher {
477 id: launcher
479 readonly property bool dashSwipe: progress > 0
481 anchors.top: parent.top
482 anchors.bottom: parent.bottom
483 width: parent.width
484 dragAreaWidth: shell.edgeSize
485 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
487 onDashItemSelected: {
488 if (edgeDemo.running)
489 return;
491 showHome()
492 }
493 onDash: {
494 if (stages.shown) {
495 stages.hide();
496 launcher.hide();
497 }
498 }
499 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("applications.scope", false, true)
500 onLauncherApplicationSelected:{
501 if (edgeDemo.running)
502 return;
504 greeter.hide()
505 shell.activateApplication(appId)
506 }
507 onShownChanged: {
508 if (shown) {
509 panel.indicators.hide()
510 hud.hide()
511 bottombar.hide()
512 }
513 }
514 }
516 Notifications {
517 id: notifications
519 model: NotificationBackend.Model
520 anchors {
521 top: parent.top
522 right: parent.right
523 bottom: parent.bottom
524 leftMargin: units.gu(1)
525 rightMargin: units.gu(1)
526 topMargin: panel.panelHeight + units.gu(1)
527 }
528 states: [
529 State {
530 name: "narrow"
531 when: overlay.width <= units.gu(60)
532 AnchorChanges { target: notifications; anchors.left: parent.left }
533 },
534 State {
535 name: "wide"
536 when: overlay.width > units.gu(60)
537 AnchorChanges { target: notifications; anchors.left: undefined }
538 PropertyChanges { target: notifications; width: units.gu(38) }
539 }
540 ]
542 InputFilterArea {
543 anchors { left: parent.left; right: parent.right }
544 height: parent.contentHeight
545 blockInput: height > 0
546 }
547 }
548 }
550 focus: true
551 onFocusChanged: if (!focus) forceActiveFocus();
553 InputFilterArea {
554 anchors {
555 top: parent.top
556 bottom: parent.bottom
557 left: parent.left
558 }
559 width: shell.edgeSize
560 blockInput: true
561 }
563 InputFilterArea {
564 anchors {
565 top: parent.top
566 bottom: parent.bottom
567 right: parent.right
568 }
569 width: shell.edgeSize
570 blockInput: true
571 }
573 Binding {
574 target: i18n
575 property: "domain"
576 value: "unity8"
577 }
579 OSKController {
580 anchors.topMargin: panel.panelHeight
581 anchors.fill: parent // as needs to know the geometry of the shell
582 }
584 //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
585 MouseArea {
586 anchors.bottom: parent.bottom
587 anchors.left: parent.left
588 anchors.right: parent.right
589 height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0
591 enabled: ApplicationManager.keyboardVisible
592 }
594 Label {
595 anchors.centerIn: parent
596 visible: ApplicationManager.fake ? ApplicationManager.fake : false
597 text: "EARLY ALPHA\nNOT READY FOR USE"
598 color: "lightgrey"
599 opacity: 0.2
600 font.weight: Font.Black
601 horizontalAlignment: Text.AlignHCenter
602 verticalAlignment: Text.AlignVCenter
603 fontSizeMode: Text.Fit
604 rotation: -45
605 scale: Math.min(parent.width, parent.height) / width
606 }
608 EdgeDemo {
609 id: edgeDemo
610 greeter: greeter
611 launcher: launcher
612 dash: dash
613 indicators: panel.indicators
614 underlay: underlay
615 }
617 Connections {
618 target: SessionBroadcast
619 onShowHome: showHome()
620 }