on e2245de help sign in
unity8/qml/Shell.qml
812 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
40 objectName: "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 dashShown: dash.shown
53 property bool stageScreenshotsReady: {
54 if (sideStage.shown) {
55 if (mainStage.applications.count > 0) {
56 return mainStage.usingScreenshots && sideStage.usingScreenshots;
57 } else {
58 return sideStage.usingScreenshots;
59 }
60 } else {
61 return mainStage.usingScreenshots;
62 }
63 }
65 property var applicationManager: ApplicationManagerWrapper {}
67 Binding {
68 target: LauncherModel
69 property: "applicationManager"
70 value: ApplicationManager
71 }
73 Component.onCompleted: {
74 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
76 applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
78 // FIXME: if application focused before shell starts, shell draws on top of it only.
79 // We should detect already running applications on shell start and bring them to the front.
80 applicationManager.unfocusCurrentApplication();
82 applicationManager.setMainStageAppRect(mainStageAppRect);
83 applicationManager.setSideStageAppRect(sideStageAppRect);
84 }
86 readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
87 || !!applicationManager.sideStageFocusedApplication
88 // Used for autopilot testing.
89 readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
91 readonly property bool fullscreenMode: {
92 if (greeter.shown || lockscreen.shown) {
93 return false;
94 } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
95 return mainStage.switchingFromFullscreenToFullscreen;
96 } else if (applicationManager.mainStageFocusedApplication) {
97 return applicationManager.mainStageFocusedApplication.fullscreen;
98 } else {
99 return false;
100 }
101 }
103 function activateApplication(appId, argument) {
104 if (applicationManager) {
105 // For newly started applications, as it takes them time to draw their first frame
106 // we add a delay before we hide the animation screenshots to compensate.
107 var addDelay = !applicationManager.getApplicationFromDesktopFile(appId);
109 var application;
110 application = applicationManager.activateApplication(appId, argument);
111 if (application == null) {
112 return;
113 }
114 if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
115 mainStage.activateApplication(appId, addDelay);
116 } else {
117 sideStage.activateApplication(appId, addDelay);
118 }
119 stages.show();
120 }
121 }
123 Connections {
124 target: applicationManager
125 onFocusRequested: activateApplication(appId)
126 }
128 GSettings {
129 id: backgroundSettings
130 schema.id: "org.gnome.desktop.background"
131 }
132 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
133 onGSettingsPictureChanged: {
134 shell.background = gSettingsPicture
135 }
137 VolumeControl {
138 id: volumeControl
139 }
141 Keys.onVolumeUpPressed: volumeControl.volumeUp()
142 Keys.onVolumeDownPressed: volumeControl.volumeDown()
144 Item {
145 id: underlay
146 objectName: "underlay"
147 anchors.fill: parent
149 // Whether the underlay is fully covered by opaque UI elements.
150 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
152 readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
153 || (sideStage.applications && sideStage.applications.count > 0))
155 // Whether the user should see the topmost application surface (if there's one at all).
156 readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
157 && !mainStage.usingScreenshots // but want sideStage animating over app surface
161 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
162 // through the translucent parts of the shell surface.
163 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
165 Rectangle {
166 anchors.fill: parent
167 color: "black"
168 opacity: dash.disappearingAnimationProgress
169 }
171 Image {
172 anchors.fill: dash
173 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
174 fillMode: Image.PreserveAspectCrop
175 horizontalAlignment: Image.AlignRight
176 verticalAlignment: Image.AlignTop
177 }
179 Dash {
180 id: dash
181 objectName: "dash"
183 available: !greeter.shown && !lockscreen.shown
184 hides: [stages, launcher, panel.indicators]
185 shown: disappearingAnimationProgress !== 1.0
186 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
187 // FIXME: unfocus all applications when going back to the dash
188 onEnabledChanged: {
189 if (enabled) {
190 shell.applicationManager.unfocusCurrentApplication()
191 }
192 }
194 anchors {
195 fill: parent
196 topMargin: panel.panelHeight
197 }
199 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
200 opacity: 1.0 - disappearingAnimationProgress
201 property real disappearingAnimationProgress: {
202 if (greeter.shown) {
203 return greeter.showProgress;
204 } else {
205 return stagesOuterContainer.showProgress;
206 }
207 }
209 // FIXME: only necessary because stagesOuterContainer.showProgress and
210 // greeterRevealer.animatedProgress are not animated
211 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
212 }
213 }
215 Item {
216 id: stagesOuterContainer
218 width: parent.width
219 height: parent.height
220 x: launcher.progress
221 Behavior on x {SmoothedAnimation{velocity: 600}}
223 property real showProgress:
224 MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
226 Showable {
227 id: stages
228 objectName: "stages"
230 x: width
232 property bool fullyShown: shown && x == 0 && parent.x == 0
233 property bool fullyHidden: !shown && x == width
234 available: !greeter.shown
235 hides: [panel.indicators]
236 shown: false
237 opacity: 1.0
238 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
239 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }
241 width: parent.width
242 height: parent.height
244 // close the stages when no focused application remains
245 Connections {
246 target: shell.applicationManager
247 onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
248 onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
249 ignoreUnknownSignals: true
250 }
252 function closeIfNoApplications() {
253 if (!shell.applicationManager.mainStageFocusedApplication
254 && !shell.applicationManager.sideStageFocusedApplication
255 && shell.applicationManager.mainStageApplications.count == 0
256 && shell.applicationManager.sideStageApplications.count == 0) {
257 stages.hide();
258 }
259 }
261 // show the stages when an application gets the focus
262 Connections {
263 target: shell.applicationManager
264 onMainStageFocusedApplicationChanged: {
265 if (shell.applicationManager.mainStageFocusedApplication) {
266 mainStage.show();
267 stages.show();
268 }
269 }
270 onSideStageFocusedApplicationChanged: {
271 if (shell.applicationManager.sideStageFocusedApplication) {
272 sideStage.show();
273 stages.show();
274 }
275 }
276 ignoreUnknownSignals: true
277 }
279 Stage {
280 id: mainStage
282 anchors.fill: parent
283 fullyShown: stages.fullyShown
284 fullyHidden: stages.fullyHidden
285 shouldUseScreenshots: !fullyShown
286 rightEdgeEnabled: !sideStage.enabled
288 applicationManager: shell.applicationManager
289 rightEdgeDraggingAreaWidth: shell.edgeSize
290 normalApplicationY: shell.panelHeight
292 shown: true
293 function show() {
294 stages.show();
295 }
296 function hide() {
297 }
299 // FIXME: workaround the fact that focusing a main stage application
300 // raises its surface on top of all other surfaces including the ones
301 // that belong to side stage applications.
302 onFocusedApplicationChanged: {
303 if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
304 shell.applicationManager.focusApplication(sideStage.focusedApplication);
305 }
306 }
307 }
309 SideStage {
310 id: sideStage
312 applicationManager: shell.applicationManager
313 rightEdgeDraggingAreaWidth: shell.edgeSize
314 normalApplicationY: shell.panelHeight
316 onShownChanged: {
317 if (!shown && mainStage.applications.count == 0) {
318 stages.hide();
319 }
320 }
321 // FIXME: when hiding the side stage, refocus the main stage
322 // application so that it goes in front of the side stage
323 // application and hides it
324 onFullyShownChanged: {
325 if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
326 shell.applicationManager.focusApplication(mainStage.focusedApplication);
327 }
328 }
330 enabled: shell.width >= units.gu(60)
331 visible: enabled
332 fullyShown: stages.fullyShown && shown
333 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
334 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
336 available: !greeter.shown && !lockscreen.shown && enabled
337 hides: [launcher, panel.indicators]
338 shown: false
339 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
340 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
342 width: units.gu(40)
343 height: stages.height
344 handleExpanded: sideStageRevealer.pressed
346 Connections {
347 target: sideStage.applications
348 onCountChanged: {
349 if (sideStage.applications.count == 0 && sideStage.shown) { // if all SS app closed, hide side stage
350 sideStage.hide();
351 }
352 }
353 }
354 }
356 Revealer {
357 id: sideStageRevealer
359 enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
360 && sideStage.available
361 direction: Qt.RightToLeft
362 openedValue: parent.width - sideStage.width
363 hintDisplacement: units.gu(3)
364 /* The size of the sidestage handle needs to be bigger than the
365 typical size used for edge detection otherwise it is really
366 hard to grab.
367 */
368 handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
369 closedValue: parent.width + sideStage.handleSizeCollapsed
370 target: sideStage
371 x: parent.width - width
372 width: sideStage.width + handleSize * 0.7
373 height: sideStage.height
374 orientation: Qt.Horizontal
375 }
377 // Defines the rectangle occupied by an application on the main or side stage.
378 // Used only to inform the ApplicationManager. Can be removed when the proper
379 // architecture is in place (unity8 as mir compositor).
380 Item {
381 id: mainStageAppRect
382 x: mainStage.x
383 y: mainStage.y + mainStage.normalApplicationY
384 width: mainStage.width
385 height: mainStage.height
386 visible: false; enabled: false
387 }
388 Item {
389 id: sideStageAppRect
390 x: sideStageRevealer.openedValue
391 y: sideStage.y + sideStage.normalApplicationY
392 width: sideStage.width
393 height: sideStage.height
394 visible: false; enabled: false
395 }
397 DragHandle {
398 id: stagesDragHandle
400 anchors.top: parent.top
401 anchors.bottom: parent.bottom
402 anchors.right: parent.left
404 width: shell.edgeSize
405 direction: Direction.Leftwards
406 enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled
407 property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
409 maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
410 // Make autocompletion impossible when !haveApps
411 edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
412 }
413 }
414 }
416 Lockscreen {
417 id: lockscreen
418 objectName: "lockscreen"
420 readonly property int backgroundTopMargin: -panel.panelHeight
422 hides: [launcher, panel.indicators, hud]
423 shown: false
424 enabled: true
425 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
426 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
427 y: panel.panelHeight
428 x: required ? 0 : - width
429 width: parent.width
430 height: parent.height - panel.panelHeight
431 background: shell.background
432 pinLength: 4
434 onEntered: LightDM.Greeter.respond(passphrase);
435 onCancel: greeter.show()
437 Component.onCompleted: {
438 if (LightDM.Users.count == 1) {
439 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
440 greeter.selected(0)
441 }
442 }
443 }
445 Connections {
446 target: LightDM.Greeter
448 onShowPrompt: {
449 if (LightDM.Users.count == 1) {
450 // TODO: There's no better way for now to determine if its a PIN or a passphrase.
451 if (text == "PIN") {
452 lockscreen.alphaNumeric = false
453 } else {
454 lockscreen.alphaNumeric = true
455 }
456 lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
457 lockscreen.show();
458 }
459 }
461 onAuthenticationComplete: {
462 if (LightDM.Greeter.promptless) {
463 return;
464 }
465 if (LightDM.Greeter.authenticated) {
466 lockscreen.hide();
467 } else {
468 lockscreen.clear(true);
469 }
470 }
471 }
473 Greeter {
474 id: greeter
475 objectName: "greeter"
477 available: true
478 hides: [launcher, panel.indicators, hud]
479 shown: true
481 defaultBackground: shell.background
483 y: panel.panelHeight
484 width: parent.width
485 height: parent.height - panel.panelHeight
487 dragHandleWidth: shell.edgeSize
489 property var previousMainApp: null
490 property var previousSideApp: null
492 function removeApplicationFocus() {
493 greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
494 if (sideStage.fullyShown) { // if side stage hidden, don't focus it on restore
495 greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
496 }
497 applicationManager.unfocusCurrentApplication();
498 }
500 function restoreApplicationFocus() {
501 if (greeter.previousMainApp) {
502 applicationManager.focusApplication(greeter.previousMainApp);
503 greeter.previousMainApp = null;
504 }
505 if (greeter.previousSideApp) {
506 applicationManager.focusApplication(greeter.previousSideApp);
507 greeter.previousSideApp = null;
508 }
509 }
511 onShownChanged: {
512 if (shown) {
513 lockscreen.reset();
514 // If there is only one user, we start authenticating with that one here.
515 // If there are more users, the Greeter will handle that
516 if (LightDM.Users.count == 1) {
517 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
518 greeter.selected(0);
519 }
520 greeter.forceActiveFocus();
521 removeApplicationFocus();
522 } else {
523 restoreApplicationFocus();
524 }
525 }
527 onUnlocked: greeter.hide()
528 onSelected: {
529 // Update launcher items for new user
530 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
531 AccountsService.user = user;
532 LauncherModel.setUser(user);
533 }
535 onLeftTeaserPressedChanged: {
536 if (leftTeaserPressed) {
537 launcher.tease();
538 }
539 }
541 Connections {
542 target: applicationManager
543 ignoreUnknownSignals: true
544 // If any app is focused when greeter is open, it's due to a user action
545 // like a snap decision (say, an incoming call).
546 // TODO: these should be protected to only unlock for certain applications / certain usecases
547 // potentially only in connection with a notification.
548 onMainStageFocusedApplicationChanged: {
549 if (greeter.shown && applicationManager.mainStageFocusedApplication) {
550 greeter.previousMainApp = null // make way for new focused app
551 greeter.previousSideApp = null
552 greeter.hide()
553 }
554 }
555 onSideStageFocusedApplicationChanged: {
556 if (greeter.shown && applicationManager.sideStageFocusedApplication) {
557 greeter.previousMainApp = null // make way for new focused app
558 greeter.previousSideApp = null
559 greeter.hide()
560 }
561 }
562 }
563 }
565 InputFilterArea {
566 anchors.fill: parent
567 blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown
568 || panel.indicators.shown || hud.shown
569 }
571 Connections {
572 id: powerConnection
573 target: Powerd
575 onDisplayPowerStateChange: {
576 // We ignore any display-off signals when the proximity sensor
577 // is active. This usually indicates something like a phone call.
578 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
579 greeter.showNow();
580 }
582 // No reason to chew demo CPU when user isn't watching
583 if (status == Powerd.Off) {
584 edgeDemo.paused = true;
585 } else if (status == Powerd.On) {
586 edgeDemo.paused = false;
587 }
588 }
589 }
591 function showHome() {
592 var animate = !greeter.shown && !stages.shown
593 greeter.hide()
594 dash.setCurrentScope("clickscope", animate, false)
595 stages.hide()
596 }
598 function hideIndicatorMenu(delay) {
599 panel.hideIndicatorMenu(delay);
600 }
602 Item {
603 id: overlay
605 anchors.fill: parent
607 Panel {
608 id: panel
609 anchors.fill: parent //because this draws indicator menus
610 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
611 indicators {
612 hides: [launcher]
613 available: edgeDemo.panelEnabled
614 contentEnabled: edgeDemo.panelContentEnabled
615 }
616 fullscreenMode: shell.fullscreenMode
617 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
619 InputFilterArea {
620 anchors {
621 top: parent.top
622 left: parent.left
623 right: parent.right
624 }
625 height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
626 blockInput: true
627 }
628 }
630 Hud {
631 id: hud
633 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
634 height: parent.height
636 available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && edgeDemo.dashEnabled
637 shown: false
638 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
639 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
641 Connections {
642 target: shell.applicationManager
643 onMainStageFocusedApplicationChanged: hud.hide()
644 onSideStageFocusedApplicationChanged: hud.hide()
645 }
646 }
648 Revealer {
649 id: hudRevealer
651 enabled: hud.shown
652 width: hud.width
653 anchors.left: hud.left
654 height: parent.height
655 target: hud.revealerTarget
656 closedValue: height
657 openedValue: 0
658 direction: Qt.RightToLeft
659 orientation: Qt.Vertical
660 handleSize: hud.handleHeight
661 onCloseClicked: target.hide()
662 }
664 Bottombar {
665 id: bottombar
666 theHud: hud
667 anchors.fill: parent
668 enabled: hud.available
669 applicationIsOnForeground: applicationFocused
670 }
672 InputFilterArea {
673 blockInput: launcher.shown
674 anchors {
675 top: parent.top
676 bottom: parent.bottom
677 left: parent.left
678 }
679 width: launcher.width
680 }
682 Launcher {
683 id: launcher
685 readonly property bool dashSwipe: progress > 0
687 anchors.top: parent.top
688 anchors.bottom: parent.bottom
689 width: parent.width
690 dragAreaWidth: shell.edgeSize
691 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
693 onShowDashHome: {
694 if (edgeDemo.running)
695 return;
697 showHome()
698 }
699 onDash: {
700 if (stages.shown) {
701 stages.hide();
702 launcher.hide();
703 }
704 }
705 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
706 onLauncherApplicationSelected:{
707 if (edgeDemo.running)
708 return;
710 greeter.hide()
711 shell.activateApplication(appId)
712 }
713 onShownChanged: {
714 if (shown) {
715 panel.indicators.hide()
716 hud.hide()
717 bottombar.hide()
718 }
719 }
720 }
722 Notifications {
723 id: notifications
725 model: NotificationBackend.Model
726 margin: units.gu(1)
728 anchors {
729 top: parent.top
730 right: parent.right
731 bottom: parent.bottom
732 topMargin: panel.panelHeight
733 }
734 states: [
735 State {
736 name: "narrow"
737 when: overlay.width <= units.gu(60)
738 AnchorChanges { target: notifications; anchors.left: parent.left }
739 },
740 State {
741 name: "wide"
742 when: overlay.width > units.gu(60)
743 AnchorChanges { target: notifications; anchors.left: undefined }
744 PropertyChanges { target: notifications; width: units.gu(38) }
745 }
746 ]
748 InputFilterArea {
749 anchors { left: parent.left; right: parent.right }
750 height: parent.contentHeight
751 blockInput: height > 0
752 }
753 }
754 }
756 focus: true
757 onFocusChanged: if (!focus) forceActiveFocus();
759 InputFilterArea {
760 anchors {
761 top: parent.top
762 bottom: parent.bottom
763 left: parent.left
764 }
765 width: shell.edgeSize
766 blockInput: true
767 }
769 InputFilterArea {
770 anchors {
771 top: parent.top
772 bottom: parent.bottom
773 right: parent.right
774 }
775 width: shell.edgeSize
776 blockInput: true
777 }
779 Binding {
780 target: i18n
781 property: "domain"
782 value: "unity8"
783 }
785 Label {
786 anchors.centerIn: parent
787 visible: applicationManager.fake
788 text: "EARLY ALPHA\nNOT READY FOR USE"
789 color: "lightgrey"
790 opacity: 0.2
791 font.weight: Font.Black
792 horizontalAlignment: Text.AlignHCenter
793 verticalAlignment: Text.AlignVCenter
794 fontSizeMode: Text.Fit
795 rotation: -45
796 scale: Math.min(parent.width, parent.height) / width
797 }
799 EdgeDemo {
800 id: edgeDemo
801 greeter: greeter
802 launcher: launcher
803 dash: dash
804 indicators: panel.indicators
805 underlay: underlay
806 }
808 Connections {
809 target: SessionBroadcast
810 onShowHome: showHome()
811 }