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