on dab0d5b help sign in
unity8/qml/Shell.qml
813 lines 29 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: 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 dashShown: dash.shown
52 property bool stageScreenshotsReady: {
53 if (sideStage.shown) {
54 if (mainStage.applications.count > 0) {
55 return mainStage.usingScreenshots && sideStage.usingScreenshots;
56 } else {
57 return sideStage.usingScreenshots;
58 }
59 } else {
60 return mainStage.usingScreenshots;
61 }
62 }
64 property var applicationManager: ApplicationManagerWrapper {}
66 Binding {
67 target: LauncherModel
68 property: "applicationManager"
69 value: ApplicationManager
70 }
72 Component.onCompleted: {
73 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
75 applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
77 // FIXME: if application focused before shell starts, shell draws on top of it only.
78 // We should detect already running applications on shell start and bring them to the front.
79 applicationManager.unfocusCurrentApplication();
80 }
82 readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
83 || !!applicationManager.sideStageFocusedApplication
84 // Used for autopilot testing.
85 readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
87 readonly property bool fullscreenMode: {
88 if (greeter.shown || lockscreen.shown) {
89 return false;
90 } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
91 return mainStage.switchingFromFullscreenToFullscreen;
92 } else if (applicationManager.mainStageFocusedApplication) {
93 return applicationManager.mainStageFocusedApplication.fullscreen;
94 } else {
95 return false;
96 }
97 }
99 function activateApplication(appId, argument) {
100 if (applicationManager) {
101 // For newly started applications, as it takes them time to draw their first frame
102 // we add a delay before we hide the animation screenshots to compensate.
103 var addDelay = !applicationManager.getApplicationFromDesktopFile(appId);
105 var application;
106 application = applicationManager.activateApplication(appId, argument);
107 if (application == null) {
108 return;
109 }
110 if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
111 mainStage.activateApplication(appId, addDelay);
112 } else {
113 sideStage.activateApplication(appId, addDelay);
114 }
115 stages.show();
116 }
117 }
119 GSettings {
120 id: backgroundSettings
121 schema.id: "org.gnome.desktop.background"
122 }
123 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
124 onGSettingsPictureChanged: {
125 shell.background = gSettingsPicture
126 }
128 // This is a dummy image that is needed to determine if the picture url
129 // in backgroundSettings points to a valid picture file.
130 // We can't do this with the real background image because setting a
131 // new source in onStatusChanged triggers a binding loop detection
132 // inside Image, which causes it not to render even though a valid source
133 // would be set. We don't mind about this image staying black and just
134 // use it for verification to populate the source for the real
135 // background image.
136 Image {
137 source: shell.background
138 height: 0
139 width: 0
140 sourceSize.height: 0
141 sourceSize.width: 0
142 onStatusChanged: {
143 if (status == Image.Error && source != shell.defaultBackground) {
144 shell.background = defaultBackground
145 }
146 }
147 }
149 VolumeControl {
150 id: volumeControl
151 }
153 Keys.onVolumeUpPressed: volumeControl.volumeUp()
154 Keys.onVolumeDownPressed: volumeControl.volumeDown()
156 Item {
157 id: underlay
158 objectName: "underlay"
159 anchors.fill: parent
161 // Whether the underlay is fully covered by opaque UI elements.
162 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
164 readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
165 || (sideStage.applications && sideStage.applications.count > 0))
167 // Whether the user should see the topmost application surface (if there's one at all).
168 readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
169 && !mainStage.usingScreenshots // but want sideStage animating over app surface
173 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
174 // through the translucent parts of the shell surface.
175 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
177 CrossFadeImage {
178 id: backgroundImage
179 objectName: "backgroundImage"
181 anchors.fill: parent
182 source: shell.background
183 fillMode: Image.PreserveAspectCrop
184 }
186 Rectangle {
187 anchors.fill: parent
188 color: "black"
189 opacity: dash.disappearingAnimationProgress
190 }
192 Dash {
193 id: dash
194 objectName: "dash"
196 available: !greeter.shown && !lockscreen.shown
197 hides: [stages, launcher, panel.indicators]
198 shown: disappearingAnimationProgress !== 1.0
199 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
200 // FIXME: unfocus all applications when going back to the dash
201 onEnabledChanged: {
202 if (enabled) {
203 shell.applicationManager.unfocusCurrentApplication()
204 }
205 }
207 anchors {
208 fill: parent
209 topMargin: panel.panelHeight
210 }
212 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
213 opacity: 1.0 - disappearingAnimationProgress
214 property real disappearingAnimationProgress: stagesOuterContainer.showProgress
216 // FIXME: only necessary because stagesOuterContainer.showProgress
217 // is not animated
218 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
219 }
220 }
222 Item {
223 id: stagesOuterContainer
225 width: parent.width
226 height: parent.height
227 x: launcher.progress
228 Behavior on x {SmoothedAnimation{velocity: 600}}
230 property real showProgress:
231 MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
233 Showable {
234 id: stages
235 objectName: "stages"
237 x: width
239 property bool fullyShown: shown && x == 0 && parent.x == 0
240 property bool fullyHidden: !shown && x == width
241 available: !greeter.shown
242 hides: [panel.indicators]
243 shown: false
244 opacity: 1.0
245 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
246 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }
248 width: parent.width
249 height: parent.height
251 // close the stages when no focused application remains
252 Connections {
253 target: shell.applicationManager
254 onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
255 onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
256 ignoreUnknownSignals: true
257 }
259 function closeIfNoApplications() {
260 if (!shell.applicationManager.mainStageFocusedApplication
261 && !shell.applicationManager.sideStageFocusedApplication
262 && shell.applicationManager.mainStageApplications.count == 0
263 && shell.applicationManager.sideStageApplications.count == 0) {
264 stages.hide();
265 }
266 }
268 // show the stages when an application gets the focus
269 Connections {
270 target: shell.applicationManager
271 onMainStageFocusedApplicationChanged: {
272 if (shell.applicationManager.mainStageFocusedApplication) {
273 mainStage.show();
274 stages.show();
275 }
276 }
277 onSideStageFocusedApplicationChanged: {
278 if (shell.applicationManager.sideStageFocusedApplication) {
279 sideStage.show();
280 stages.show();
281 }
282 }
283 ignoreUnknownSignals: true
284 }
286 Stage {
287 id: mainStage
289 anchors.fill: parent
290 fullyShown: stages.fullyShown
291 fullyHidden: stages.fullyHidden
292 shouldUseScreenshots: !fullyShown
293 rightEdgeEnabled: !sideStage.enabled
295 applicationManager: shell.applicationManager
296 rightEdgeDraggingAreaWidth: shell.edgeSize
297 normalApplicationY: shell.panelHeight
299 shown: true
300 function show() {
301 stages.show();
302 }
303 function hide() {
304 }
306 // FIXME: workaround the fact that focusing a main stage application
307 // raises its surface on top of all other surfaces including the ones
308 // that belong to side stage applications.
309 onFocusedApplicationChanged: {
310 if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
311 shell.applicationManager.focusApplication(sideStage.focusedApplication);
312 }
313 }
314 }
316 SideStage {
317 id: sideStage
319 applicationManager: shell.applicationManager
320 rightEdgeDraggingAreaWidth: shell.edgeSize
321 normalApplicationY: shell.panelHeight
323 onShownChanged: {
324 if (!shown && mainStage.applications.count == 0) {
325 stages.hide();
326 }
327 }
328 // FIXME: when hiding the side stage, refocus the main stage
329 // application so that it goes in front of the side stage
330 // application and hides it
331 onFullyShownChanged: {
332 if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
333 shell.applicationManager.focusApplication(mainStage.focusedApplication);
334 }
335 }
337 enabled: shell.width >= units.gu(100)
338 visible: enabled
339 fullyShown: stages.fullyShown && shown
340 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
341 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
343 available: !greeter.shown && !lockscreen.shown && enabled
344 hides: [launcher, panel.indicators]
345 shown: false
346 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
347 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
349 width: units.gu(40)
350 height: stages.height
351 handleExpanded: sideStageRevealer.pressed
352 }
354 Revealer {
355 id: sideStageRevealer
357 enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
358 && sideStage.available
359 direction: Qt.RightToLeft
360 openedValue: parent.width - sideStage.width
361 hintDisplacement: units.gu(3)
362 /* The size of the sidestage handle needs to be bigger than the
363 typical size used for edge detection otherwise it is really
364 hard to grab.
365 */
366 handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
367 closedValue: parent.width + sideStage.handleSizeCollapsed
368 target: sideStage
369 x: parent.width - width
370 width: sideStage.width + handleSize * 0.7
371 height: sideStage.height
372 orientation: Qt.Horizontal
373 }
375 DragHandle {
376 id: stagesDragHandle
378 anchors.top: parent.top
379 anchors.bottom: parent.bottom
380 anchors.right: parent.left
382 width: shell.edgeSize
383 direction: Direction.Leftwards
384 enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled
385 property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
387 maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
388 // Make autocompletion impossible when !haveApps
389 edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
390 }
391 }
392 }
394 Lockscreen {
395 id: lockscreen
396 objectName: "lockscreen"
398 readonly property int backgroundTopMargin: -panel.panelHeight
400 hides: [launcher, panel.indicators, hud]
401 shown: false
402 enabled: true
403 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
404 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
405 y: panel.panelHeight
406 x: required ? 0 : - width
407 width: parent.width
408 height: parent.height - panel.panelHeight
409 background: shell.background
410 pinLength: 4
412 onEntered: LightDM.Greeter.respond(passphrase);
413 onCancel: greeter.show()
415 Component.onCompleted: {
416 if (LightDM.Users.count == 1) {
417 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
418 }
419 }
420 }
422 Connections {
423 target: LightDM.Greeter
425 onShowPrompt: {
426 if (LightDM.Users.count == 1) {
427 // TODO: There's no better way for now to determine if its a PIN or a passphrase.
428 if (text == "PIN") {
429 lockscreen.alphaNumeric = false
430 } else {
431 lockscreen.alphaNumeric = true
432 }
433 lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
434 lockscreen.show();
435 }
436 }
438 onAuthenticationComplete: {
439 if (LightDM.Greeter.promptless) {
440 return;
441 }
442 if (LightDM.Greeter.authenticated) {
443 lockscreen.hide();
444 } else {
445 lockscreen.clear(true);
446 }
447 }
448 }
450 Item {
451 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
452 width: parent.width
453 height: parent.height
454 x: launcher.progress
455 Behavior on x {SmoothedAnimation{velocity: 600}}
457 Rectangle {
458 anchors.fill: parent
459 color: "black"
460 opacity: (1 - Math.abs(greeter.x) / shell.width) * 0.8
461 }
463 Greeter {
464 id: greeter
465 objectName: "greeter"
467 available: true
468 hides: [launcher, panel.indicators, hud]
469 shown: true
471 defaultBackground: shell.background
473 y: panel.panelHeight
474 width: parent.width
475 height: parent.height - panel.panelHeight
477 dragHandleWidth: shell.edgeSize
479 property var previousMainApp: null
480 property var previousSideApp: null
482 function removeApplicationFocus() {
483 greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
484 greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
485 applicationManager.unfocusCurrentApplication();
486 }
488 function restoreApplicationFocus() {
489 if (greeter.previousMainApp) {
490 applicationManager.focusApplication(greeter.previousMainApp);
491 greeter.previousMainApp = null;
492 }
493 if (greeter.previousSideApp) {
494 applicationManager.focusApplication(greeter.previousSideApp);
495 greeter.previousSideApp = null;
496 }
497 }
499 onShownChanged: {
500 if (shown) {
501 lockscreen.reset();
502 // If there is only one user, we start authenticating with that one here.
503 // If there are more users, the Greeter will handle that
504 if (LightDM.Users.count == 1) {
505 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
506 }
507 greeter.forceActiveFocus();
508 removeApplicationFocus();
509 } else {
510 restoreApplicationFocus();
511 }
512 }
514 onUnlocked: greeter.hide()
515 onSelected: {
516 // Update launcher items for new user
517 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
518 AccountsService.user = user;
519 LauncherModel.setUser(user);
520 }
522 onTease: launcher.tease() //launcher.state = "visible"
524 Connections {
525 target: applicationManager
526 ignoreUnknownSignals: true
527 // If any app is focused when greeter is open, it's due to a user action
528 // like a snap decision (say, an incoming call).
529 // TODO: these should be protected to only unlock for certain applications / certain usecases
530 // potentially only in connection with a notification.
531 onMainStageFocusedApplicationChanged: {
532 if (greeter.shown && applicationManager.mainStageFocusedApplication) {
533 greeter.previousMainApp = null // make way for new focused app
534 greeter.previousSideApp = null
535 greeter.hide()
536 }
537 }
538 onSideStageFocusedApplicationChanged: {
539 if (greeter.shown && applicationManager.sideStageFocusedApplication) {
540 greeter.previousMainApp = null // make way for new focused app
541 greeter.previousSideApp = null
542 greeter.hide()
543 }
544 }
545 }
546 }
547 }
549 InputFilterArea {
550 anchors.fill: parent
551 blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown
552 || panel.indicators.shown || hud.shown
553 }
555 Connections {
556 id: powerConnection
557 target: Powerd
559 onDisplayPowerStateChange: {
560 // We ignore any display-off signals when the proximity sensor
561 // is active. This usually indicates something like a phone call.
562 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
563 greeter.showNow();
564 }
566 // No reason to chew demo CPU when user isn't watching
567 if (status == Powerd.Off) {
568 edgeDemo.paused = true;
569 } else if (status == Powerd.On) {
570 edgeDemo.paused = false;
571 }
572 }
573 }
575 function showHome() {
576 var animate = !greeter.shown && !stages.shown
577 greeter.hide()
578 dash.setCurrentScope("home.scope", animate, false)
579 stages.hide()
580 }
582 function hideIndicatorMenu(delay) {
583 panel.hideIndicatorMenu(delay);
584 }
586 Item {
587 id: overlay
589 anchors.fill: parent
591 Panel {
592 id: panel
593 anchors.fill: parent //because this draws indicator menus
594 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
595 indicators {
596 hides: [launcher]
597 available: edgeDemo.panelEnabled
598 contentEnabled: edgeDemo.panelContentEnabled
599 }
600 fullscreenMode: shell.fullscreenMode
601 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
603 InputFilterArea {
604 anchors {
605 top: parent.top
606 left: parent.left
607 right: parent.right
608 }
609 height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
610 blockInput: true
611 }
612 }
614 Hud {
615 id: hud
617 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
618 height: parent.height
620 available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && edgeDemo.dashEnabled
621 shown: false
622 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
623 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
625 Connections {
626 target: shell.applicationManager
627 onMainStageFocusedApplicationChanged: hud.hide()
628 onSideStageFocusedApplicationChanged: hud.hide()
629 }
630 }
632 Revealer {
633 id: hudRevealer
635 enabled: hud.shown
636 width: hud.width
637 anchors.left: hud.left
638 height: parent.height
639 target: hud.revealerTarget
640 closedValue: height
641 openedValue: 0
642 direction: Qt.RightToLeft
643 orientation: Qt.Vertical
644 handleSize: hud.handleHeight
645 onCloseClicked: target.hide()
646 }
648 Bottombar {
649 id: bottombar
650 theHud: hud
651 anchors.fill: parent
652 enabled: hud.available
653 applicationIsOnForeground: applicationFocused
654 }
656 InputFilterArea {
657 blockInput: launcher.shown
658 anchors {
659 top: parent.top
660 bottom: parent.bottom
661 left: parent.left
662 }
663 width: launcher.width
664 }
666 Launcher {
667 id: launcher
669 readonly property bool dashSwipe: progress > 0
671 anchors.top: parent.top
672 anchors.bottom: parent.bottom
673 width: parent.width
674 dragAreaWidth: shell.edgeSize
675 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
677 onShowDashHome: {
678 if (edgeDemo.running)
679 return;
681 showHome()
682 }
683 onDash: {
684 if (stages.shown) {
685 stages.hide();
686 launcher.hide();
687 }
688 if (greeter.shown)
689 greeter.hideRight();
690 }
691 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("applications.scope", false, true)
692 onLauncherApplicationSelected:{
693 if (edgeDemo.running)
694 return;
696 greeter.hide()
697 shell.activateApplication(appId)
698 }
699 onShownChanged: {
700 if (shown) {
701 panel.indicators.hide()
702 hud.hide()
703 bottombar.hide()
704 }
705 }
706 }
708 Notifications {
709 id: notifications
711 model: NotificationBackend.Model
712 margin: units.gu(1)
714 anchors {
715 top: parent.top
716 right: parent.right
717 bottom: parent.bottom
718 topMargin: panel.panelHeight
719 }
720 states: [
721 State {
722 name: "narrow"
723 when: overlay.width <= units.gu(60)
724 AnchorChanges { target: notifications; anchors.left: parent.left }
725 },
726 State {
727 name: "wide"
728 when: overlay.width > units.gu(60)
729 AnchorChanges { target: notifications; anchors.left: undefined }
730 PropertyChanges { target: notifications; width: units.gu(38) }
731 }
732 ]
734 InputFilterArea {
735 anchors { left: parent.left; right: parent.right }
736 height: parent.contentHeight
737 blockInput: height > 0
738 }
739 }
740 }
742 focus: true
743 onFocusChanged: if (!focus) forceActiveFocus();
745 InputFilterArea {
746 anchors {
747 top: parent.top
748 bottom: parent.bottom
749 left: parent.left
750 }
751 width: shell.edgeSize
752 blockInput: true
753 }
755 InputFilterArea {
756 anchors {
757 top: parent.top
758 bottom: parent.bottom
759 right: parent.right
760 }
761 width: shell.edgeSize
762 blockInput: true
763 }
765 Binding {
766 target: i18n
767 property: "domain"
768 value: "unity8"
769 }
771 OSKController {
772 anchors.topMargin: panel.panelHeight
773 anchors.fill: parent // as needs to know the geometry of the shell
774 }
776 //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
777 MouseArea {
778 anchors.bottom: parent.bottom
779 anchors.left: parent.left
780 anchors.right: parent.right
781 height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0
783 enabled: shell.applicationManager && shell.applicationManager.keyboardVisible
784 }
786 Label {
787 anchors.centerIn: parent
788 visible: applicationManager.fake
789 text: "EARLY ALPHA\nNOT READY FOR USE"
790 color: "lightgrey"
791 opacity: 0.2
792 font.weight: Font.Black
793 horizontalAlignment: Text.AlignHCenter
794 verticalAlignment: Text.AlignVCenter
795 fontSizeMode: Text.Fit
796 rotation: -45
797 scale: Math.min(parent.width, parent.height) / width
798 }
800 EdgeDemo {
801 id: edgeDemo
802 greeter: greeter
803 launcher: launcher
804 dash: dash
805 indicators: panel.indicators
806 underlay: underlay
807 }
809 Connections {
810 target: SessionBroadcast
811 onShowHome: showHome()
812 }