on 43985b8 help sign in
unity8/qml/Shell.qml
801 lines 28 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 Connections {
120 target: applicationManager
121 onFocusRequested: activateApplication(appId)
122 }
124 GSettings {
125 id: backgroundSettings
126 schema.id: "org.gnome.desktop.background"
127 }
128 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
129 onGSettingsPictureChanged: {
130 shell.background = gSettingsPicture
131 }
133 VolumeControl {
134 id: volumeControl
135 }
137 Keys.onVolumeUpPressed: volumeControl.volumeUp()
138 Keys.onVolumeDownPressed: volumeControl.volumeDown()
140 Item {
141 id: underlay
142 objectName: "underlay"
143 anchors.fill: parent
145 // Whether the underlay is fully covered by opaque UI elements.
146 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
148 readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
149 || (sideStage.applications && sideStage.applications.count > 0))
151 // Whether the user should see the topmost application surface (if there's one at all).
152 readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
153 && !mainStage.usingScreenshots // but want sideStage animating over app surface
157 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
158 // through the translucent parts of the shell surface.
159 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
161 Rectangle {
162 anchors.fill: parent
163 color: "black"
164 opacity: dash.disappearingAnimationProgress
165 }
167 Image {
168 anchors.fill: dash
169 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
170 fillMode: Image.PreserveAspectCrop
171 horizontalAlignment: Image.AlignRight
172 verticalAlignment: Image.AlignTop
173 }
175 Dash {
176 id: dash
177 objectName: "dash"
179 available: !greeter.shown && !lockscreen.shown
180 hides: [stages, launcher, panel.indicators]
181 shown: disappearingAnimationProgress !== 1.0
182 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
183 // FIXME: unfocus all applications when going back to the dash
184 onEnabledChanged: {
185 if (enabled) {
186 shell.applicationManager.unfocusCurrentApplication()
187 }
188 }
190 anchors {
191 fill: parent
192 topMargin: panel.panelHeight
193 }
195 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
196 opacity: 1.0 - disappearingAnimationProgress
197 property real disappearingAnimationProgress: {
198 if (greeter.shown) {
199 return greeter.showProgress;
200 } else {
201 return stagesOuterContainer.showProgress;
202 }
203 }
205 // FIXME: only necessary because stagesOuterContainer.showProgress and
206 // greeterRevealer.animatedProgress are not animated
207 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
208 }
209 }
211 Item {
212 id: stagesOuterContainer
214 width: parent.width
215 height: parent.height
216 x: launcher.progress
217 Behavior on x {SmoothedAnimation{velocity: 600}}
219 property real showProgress:
220 MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
222 Showable {
223 id: stages
224 objectName: "stages"
226 x: width
228 property bool fullyShown: shown && x == 0 && parent.x == 0
229 property bool fullyHidden: !shown && x == width
230 available: !greeter.shown
231 hides: [panel.indicators]
232 shown: false
233 opacity: 1.0
234 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
235 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }
237 width: parent.width
238 height: parent.height
240 // close the stages when no focused application remains
241 Connections {
242 target: shell.applicationManager
243 onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
244 onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
245 ignoreUnknownSignals: true
246 }
248 function closeIfNoApplications() {
249 if (!shell.applicationManager.mainStageFocusedApplication
250 && !shell.applicationManager.sideStageFocusedApplication
251 && shell.applicationManager.mainStageApplications.count == 0
252 && shell.applicationManager.sideStageApplications.count == 0) {
253 stages.hide();
254 }
255 }
257 // show the stages when an application gets the focus
258 Connections {
259 target: shell.applicationManager
260 onMainStageFocusedApplicationChanged: {
261 if (shell.applicationManager.mainStageFocusedApplication) {
262 mainStage.show();
263 stages.show();
264 }
265 }
266 onSideStageFocusedApplicationChanged: {
267 if (shell.applicationManager.sideStageFocusedApplication) {
268 sideStage.show();
269 stages.show();
270 }
271 }
272 ignoreUnknownSignals: true
273 }
275 Stage {
276 id: mainStage
278 anchors.fill: parent
279 fullyShown: stages.fullyShown
280 fullyHidden: stages.fullyHidden
281 shouldUseScreenshots: !fullyShown
282 rightEdgeEnabled: !sideStage.enabled
284 applicationManager: shell.applicationManager
285 rightEdgeDraggingAreaWidth: shell.edgeSize
286 normalApplicationY: shell.panelHeight
288 shown: true
289 function show() {
290 stages.show();
291 }
292 function hide() {
293 }
295 // FIXME: workaround the fact that focusing a main stage application
296 // raises its surface on top of all other surfaces including the ones
297 // that belong to side stage applications.
298 onFocusedApplicationChanged: {
299 if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
300 shell.applicationManager.focusApplication(sideStage.focusedApplication);
301 }
302 }
303 }
305 SideStage {
306 id: sideStage
308 applicationManager: shell.applicationManager
309 rightEdgeDraggingAreaWidth: shell.edgeSize
310 normalApplicationY: shell.panelHeight
312 onShownChanged: {
313 if (!shown && mainStage.applications.count == 0) {
314 stages.hide();
315 }
316 }
317 // FIXME: when hiding the side stage, refocus the main stage
318 // application so that it goes in front of the side stage
319 // application and hides it
320 onFullyShownChanged: {
321 if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
322 shell.applicationManager.focusApplication(mainStage.focusedApplication);
323 }
324 }
326 enabled: shell.width >= units.gu(100)
327 visible: enabled
328 fullyShown: stages.fullyShown && shown
329 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
330 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
332 available: !greeter.shown && !lockscreen.shown && enabled
333 hides: [launcher, panel.indicators]
334 shown: false
335 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
336 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
338 width: units.gu(40)
339 height: stages.height
340 handleExpanded: sideStageRevealer.pressed
342 Connections {
343 target: sideStage.applications
344 onCountChanged: {
345 if (sideStage.applications.count == 0 && sideStage.shown) { // if all SS app closed, hide side stage
346 sideStage.hide();
347 }
348 }
349 }
350 }
352 Revealer {
353 id: sideStageRevealer
355 enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
356 && sideStage.available
357 direction: Qt.RightToLeft
358 openedValue: parent.width - sideStage.width
359 hintDisplacement: units.gu(3)
360 /* The size of the sidestage handle needs to be bigger than the
361 typical size used for edge detection otherwise it is really
362 hard to grab.
363 */
364 handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
365 closedValue: parent.width + sideStage.handleSizeCollapsed
366 target: sideStage
367 x: parent.width - width
368 width: sideStage.width + handleSize * 0.7
369 height: sideStage.height
370 orientation: Qt.Horizontal
371 }
373 DragHandle {
374 id: stagesDragHandle
376 anchors.top: parent.top
377 anchors.bottom: parent.bottom
378 anchors.right: parent.left
380 width: shell.edgeSize
381 direction: Direction.Leftwards
382 enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled
383 property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
385 maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
386 // Make autocompletion impossible when !haveApps
387 edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
388 }
389 }
390 }
392 Lockscreen {
393 id: lockscreen
394 objectName: "lockscreen"
396 readonly property int backgroundTopMargin: -panel.panelHeight
398 hides: [launcher, panel.indicators, hud]
399 shown: false
400 enabled: true
401 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
402 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
403 y: panel.panelHeight
404 x: required ? 0 : - width
405 width: parent.width
406 height: parent.height - panel.panelHeight
407 background: shell.background
408 pinLength: 4
410 onEntered: LightDM.Greeter.respond(passphrase);
411 onCancel: greeter.show()
413 Component.onCompleted: {
414 if (LightDM.Users.count == 1) {
415 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
416 }
417 }
418 }
420 Connections {
421 target: LightDM.Greeter
423 onShowPrompt: {
424 if (LightDM.Users.count == 1) {
425 // TODO: There's no better way for now to determine if its a PIN or a passphrase.
426 if (text == "PIN") {
427 lockscreen.alphaNumeric = false
428 } else {
429 lockscreen.alphaNumeric = true
430 }
431 lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
432 lockscreen.show();
433 }
434 }
436 onAuthenticationComplete: {
437 if (LightDM.Greeter.promptless) {
438 return;
439 }
440 if (LightDM.Greeter.authenticated) {
441 lockscreen.hide();
442 } else {
443 lockscreen.clear(true);
444 }
445 }
446 }
448 Greeter {
449 id: greeter
450 objectName: "greeter"
452 available: true
453 hides: [launcher, panel.indicators, hud]
454 shown: true
456 defaultBackground: shell.defaultBackground
458 y: panel.panelHeight
459 width: parent.width
460 height: parent.height - panel.panelHeight
462 dragHandleWidth: shell.edgeSize
464 property var previousMainApp: null
465 property var previousSideApp: null
467 function removeApplicationFocus() {
468 greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
469 if (sideStage.fullyShown) { // if side stage hidden, don't focus it on restore
470 greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
471 }
472 applicationManager.unfocusCurrentApplication();
473 }
475 function restoreApplicationFocus() {
476 if (greeter.previousMainApp) {
477 applicationManager.focusApplication(greeter.previousMainApp);
478 greeter.previousMainApp = null;
479 }
480 if (greeter.previousSideApp) {
481 applicationManager.focusApplication(greeter.previousSideApp);
482 greeter.previousSideApp = null;
483 }
484 }
486 onShownChanged: {
487 if (shown) {
488 lockscreen.reset();
489 // If there is only one user, we start authenticating with that one here.
490 // If there are more users, the Greeter will handle that
491 if (LightDM.Users.count == 1) {
492 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
493 }
494 greeter.forceActiveFocus();
495 removeApplicationFocus();
496 } else {
497 restoreApplicationFocus();
498 }
499 }
501 onUnlocked: greeter.hide()
502 onSelected: {
503 // Update launcher items for new user
504 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
505 AccountsService.user = user;
506 LauncherModel.setUser(user);
507 }
509 onLeftTeaserPressedChanged: {
510 if (leftTeaserPressed) {
511 launcher.tease();
512 }
513 }
515 Connections {
516 target: applicationManager
517 ignoreUnknownSignals: true
518 // If any app is focused when greeter is open, it's due to a user action
519 // like a snap decision (say, an incoming call).
520 // TODO: these should be protected to only unlock for certain applications / certain usecases
521 // potentially only in connection with a notification.
522 onMainStageFocusedApplicationChanged: {
523 if (greeter.shown && applicationManager.mainStageFocusedApplication) {
524 greeter.previousMainApp = null // make way for new focused app
525 greeter.previousSideApp = null
526 greeter.hide()
527 }
528 }
529 onSideStageFocusedApplicationChanged: {
530 if (greeter.shown && applicationManager.sideStageFocusedApplication) {
531 greeter.previousMainApp = null // make way for new focused app
532 greeter.previousSideApp = null
533 greeter.hide()
534 }
535 }
536 }
537 }
539 InputFilterArea {
540 anchors.fill: parent
541 blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown
542 || panel.indicators.shown || hud.shown
543 }
545 Connections {
546 id: powerConnection
547 target: Powerd
549 onDisplayPowerStateChange: {
550 // We ignore any display-off signals when the proximity sensor
551 // is active. This usually indicates something like a phone call.
552 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
553 greeter.showNow();
554 }
556 // No reason to chew demo CPU when user isn't watching
557 if (status == Powerd.Off) {
558 edgeDemo.paused = true;
559 } else if (status == Powerd.On) {
560 edgeDemo.paused = false;
561 }
562 }
563 }
565 function showHome() {
566 var animate = !greeter.shown && !stages.shown
567 greeter.hide()
568 dash.setCurrentScope("clickscope", animate, false)
569 stages.hide()
570 }
572 function hideIndicatorMenu(delay) {
573 panel.hideIndicatorMenu(delay);
574 }
576 Item {
577 id: overlay
579 anchors.fill: parent
581 Panel {
582 id: panel
583 anchors.fill: parent //because this draws indicator menus
584 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
585 indicators {
586 hides: [launcher]
587 available: edgeDemo.panelEnabled
588 contentEnabled: edgeDemo.panelContentEnabled
589 }
590 fullscreenMode: shell.fullscreenMode
591 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
593 InputFilterArea {
594 anchors {
595 top: parent.top
596 left: parent.left
597 right: parent.right
598 }
599 height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
600 blockInput: true
601 }
602 }
604 Hud {
605 id: hud
607 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
608 height: parent.height
610 available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && edgeDemo.dashEnabled
611 shown: false
612 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
613 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
615 Connections {
616 target: shell.applicationManager
617 onMainStageFocusedApplicationChanged: hud.hide()
618 onSideStageFocusedApplicationChanged: hud.hide()
619 }
620 }
622 Revealer {
623 id: hudRevealer
625 enabled: hud.shown
626 width: hud.width
627 anchors.left: hud.left
628 height: parent.height
629 target: hud.revealerTarget
630 closedValue: height
631 openedValue: 0
632 direction: Qt.RightToLeft
633 orientation: Qt.Vertical
634 handleSize: hud.handleHeight
635 onCloseClicked: target.hide()
636 }
638 Bottombar {
639 id: bottombar
640 theHud: hud
641 anchors.fill: parent
642 enabled: hud.available
643 applicationIsOnForeground: applicationFocused
644 }
646 InputFilterArea {
647 blockInput: launcher.shown
648 anchors {
649 top: parent.top
650 bottom: parent.bottom
651 left: parent.left
652 }
653 width: launcher.width
654 }
656 Launcher {
657 id: launcher
659 readonly property bool dashSwipe: progress > 0
661 anchors.top: parent.top
662 anchors.bottom: parent.bottom
663 width: parent.width
664 dragAreaWidth: shell.edgeSize
665 available: !greeter.locked && edgeDemo.launcherEnabled
667 onShowDashHome: {
668 if (edgeDemo.running)
669 return;
671 showHome()
672 }
673 onDash: {
674 if (stages.shown) {
675 stages.hide();
676 launcher.hide();
677 }
678 }
679 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
680 onLauncherApplicationSelected:{
681 if (edgeDemo.running)
682 return;
684 greeter.hide()
685 shell.activateApplication(appId)
686 }
687 onShownChanged: {
688 if (shown) {
689 panel.indicators.hide()
690 hud.hide()
691 bottombar.hide()
692 }
693 }
694 }
696 Notifications {
697 id: notifications
699 model: NotificationBackend.Model
700 margin: units.gu(1)
702 anchors {
703 top: parent.top
704 right: parent.right
705 bottom: parent.bottom
706 topMargin: panel.panelHeight
707 }
708 states: [
709 State {
710 name: "narrow"
711 when: overlay.width <= units.gu(60)
712 AnchorChanges { target: notifications; anchors.left: parent.left }
713 },
714 State {
715 name: "wide"
716 when: overlay.width > units.gu(60)
717 AnchorChanges { target: notifications; anchors.left: undefined }
718 PropertyChanges { target: notifications; width: units.gu(38) }
719 }
720 ]
722 InputFilterArea {
723 anchors { left: parent.left; right: parent.right }
724 height: parent.contentHeight
725 blockInput: height > 0
726 }
727 }
728 }
730 focus: true
731 onFocusChanged: if (!focus) forceActiveFocus();
733 InputFilterArea {
734 anchors {
735 top: parent.top
736 bottom: parent.bottom
737 left: parent.left
738 }
739 width: shell.edgeSize
740 blockInput: true
741 }
743 InputFilterArea {
744 anchors {
745 top: parent.top
746 bottom: parent.bottom
747 right: parent.right
748 }
749 width: shell.edgeSize
750 blockInput: true
751 }
753 Binding {
754 target: i18n
755 property: "domain"
756 value: "unity8"
757 }
759 OSKController {
760 anchors.topMargin: panel.panelHeight
761 anchors.fill: parent // as needs to know the geometry of the shell
762 }
764 //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
765 MouseArea {
766 anchors.bottom: parent.bottom
767 anchors.left: parent.left
768 anchors.right: parent.right
769 height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0
771 enabled: shell.applicationManager && shell.applicationManager.keyboardVisible
772 }
774 Label {
775 anchors.centerIn: parent
776 visible: applicationManager.fake
777 text: "EARLY ALPHA\nNOT READY FOR USE"
778 color: "lightgrey"
779 opacity: 0.2
780 font.weight: Font.Black
781 horizontalAlignment: Text.AlignHCenter
782 verticalAlignment: Text.AlignVCenter
783 fontSizeMode: Text.Fit
784 rotation: -45
785 scale: Math.min(parent.width, parent.height) / width
786 }
788 EdgeDemo {
789 id: edgeDemo
790 greeter: greeter
791 launcher: launcher
792 dash: dash
793 indicators: panel.indicators
794 underlay: underlay
795 }
797 Connections {
798 target: SessionBroadcast
799 onShowHome: showHome()
800 }