on 1482f99 help sign in
unity8/qml/Shell.qml
916 lines 34 kB history raw
1/*
2 * Copyright (C) 2013-2016 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.4
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import GSettings 1.0
29import ImageCache 0.1
30import Utils 0.1
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stages"
39import "Tutorial"
40import "Wizard"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
44import Unity.Indicators 0.1 as Indicators
45import Cursor 1.1
46import WindowManager 0.1
49StyledItem {
50 id: shell
52 theme.name: "Ubuntu.Components.Themes.SuruDark"
54 // to be set from outside
55 property int orientationAngle: 0
56 property int orientation
57 property Orientations orientations
58 property real nativeWidth
59 property real nativeHeight
60 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
61 property bool beingResized
62 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
63 property string mode: "full-greeter"
64 property alias oskEnabled: inputMethod.enabled
65 function updateFocusedAppOrientation() {
66 applicationsDisplayLoader.item.updateFocusedAppOrientation();
67 }
68 function updateFocusedAppOrientationAnimated() {
69 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
70 }
71 property bool hasMouse: false
73 // to be read from outside
74 readonly property int mainAppWindowOrientationAngle:
75 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
77 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
79 && (!greeter || !greeter.animating)
81 readonly property bool showingGreeter: greeter && greeter.shown
83 property bool startingUp: true
84 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
86 property int supportedOrientations: {
87 if (startingUp) {
88 // Ensure we don't rotate during start up
89 return Qt.PrimaryOrientation;
90 } else if (showingGreeter || notifications.topmostIsFullscreen) {
91 return Qt.PrimaryOrientation;
92 } else if (applicationsDisplayLoader.item) {
93 return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
94 } else {
95 // we just don't care
96 return Qt.PortraitOrientation
97 | Qt.LandscapeOrientation
98 | Qt.InvertedPortraitOrientation
99 | Qt.InvertedLandscapeOrientation;
100 }
101 }
103 readonly property var mainApp:
104 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
105 onMainAppChanged: {
106 if (mainApp) {
107 _onMainAppChanged(mainApp.appId);
108 }
109 }
110 Connections {
111 target: ApplicationManager
112 onFocusRequested: {
113 if (shell.mainApp && shell.mainApp.appId === appId) {
114 _onMainAppChanged(appId);
115 }
116 }
117 }
118 function _onMainAppChanged(appId) {
119 if (wizard.active && appId != "" && appId != "unity8-dash") {
120 // If this happens on first boot, we may be in the
121 // wizard while receiving a call. But a call is more
122 // important than the wizard so just bail out of it.
123 wizard.hide();
124 }
126 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
127 // If we are in the middle of a call, make dialer lockedApp and show it.
128 // This can happen if user backs out of dialer back to greeter, then
129 // launches dialer again.
130 greeter.lockedApp = appId;
131 }
132 greeter.notifyAppFocusRequested(appId);
134 panel.indicators.hide();
135 launcher.hide();
136 }
138 // For autopilot consumption
139 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
141 // Note when greeter is waiting on PAM, so that we can disable edges until
142 // we know which user data to show and whether the session is locked.
143 readonly property bool waitingOnGreeter: greeter && greeter.waiting
145 property real edgeSize: units.gu(settings.edgeDragWidth)
147 WallpaperResolver {
148 id: wallpaperResolver
149 objectName: "wallpaperResolver"
151 readonly property url defaultBackground: "file:///usr/share/backgrounds/warty-final-ubuntu.png"
152 readonly property bool hasCustomBackground: background != defaultBackground
154 // Use a cached version of the scaled-down wallpaper (as sometimes the
155 // image can be quite big compared to the device size, including for
156 // our default wallpaper). We use a name=wallpaper argument here to
157 // make sure we don't litter our cache with lots of scaled images. We
158 // only need to bother caching one at a time.
159 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
161 GSettings {
162 id: backgroundSettings
163 schema.id: "org.gnome.desktop.background"
164 }
166 candidates: [
167 AccountsService.backgroundFile,
168 backgroundSettings.pictureUri,
169 defaultBackground
170 ]
171 }
173 readonly property alias greeter: greeterLoader.item
175 function activateApplication(appId) {
176 // Either open the app in our own session, or -- if we're acting as a
177 // greeter -- ask the user's session to open it for us.
178 if (shell.mode === "greeter") {
179 activateURL("application:///" + appId + ".desktop");
180 } else {
181 startApp(appId);
182 }
183 }
185 function activateURL(url) {
186 SessionBroadcast.requestUrlStart(AccountsService.user, url);
187 greeter.notifyUserRequestedApp();
188 panel.indicators.hide();
189 }
191 function startApp(appId) {
192 if (ApplicationManager.findApplication(appId)) {
193 ApplicationManager.requestFocusApplication(appId);
194 } else {
195 ApplicationManager.startApplication(appId);
196 }
197 }
199 function startLockedApp(app) {
200 if (greeter.locked) {
201 greeter.lockedApp = app;
202 }
203 startApp(app); // locked apps are always in our same session
204 }
206 Binding {
207 target: LauncherModel
208 property: "applicationManager"
209 value: ApplicationManager
210 }
212 Component.onCompleted: {
213 finishStartUpTimer.start();
214 }
216 VolumeControl {
217 id: volumeControl
218 }
220 DashCommunicator {
221 id: dash
222 objectName: "dashCommunicator"
223 }
225 PhysicalKeysMapper {
226 id: physicalKeysMapper
227 objectName: "physicalKeysMapper"
229 onPowerKeyLongPressed: dialogs.showPowerDialog();
230 onVolumeDownTriggered: volumeControl.volumeDown();
231 onVolumeUpTriggered: volumeControl.volumeUp();
232 onScreenshotTriggered: itemGrabber.capture(shell);
233 }
235 GlobalShortcut {
236 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
237 }
239 WindowInputFilter {
240 id: inputFilter
241 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
242 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
243 }
245 WindowInputMonitor {
246 objectName: "windowInputMonitor"
247 onHomeKeyActivated: {
248 // Ignore when greeter is active, to avoid pocket presses
249 if (!greeter.active) {
250 launcher.fadeOut();
251 shell.showHome();
252 }
253 }
254 onTouchBegun: { cursor.opacity = 0; }
255 onTouchEnded: {
256 // move the (hidden) cursor to the last known touch position
257 var mappedCoords = mapFromItem(null, pos.x, pos.y);
258 cursor.x = mappedCoords.x;
259 cursor.y = mappedCoords.y;
260 cursor.mouseNeverMoved = false;
261 }
262 }
264 GSettings {
265 id: settings
266 schema.id: "com.canonical.Unity8"
267 }
269 Item {
270 id: stages
271 objectName: "stages"
272 width: parent.width
273 height: parent.height
275 TopLevelSurfaceList {
276 id: topLevelSurfaceList
277 objectName: "topLevelSurfaceList"
278 applicationsModel: ApplicationManager
279 }
281 Loader {
282 id: applicationsDisplayLoader
283 objectName: "applicationsDisplayLoader"
284 anchors.fill: parent
286 // When we have a locked app, we only want to show that one app.
287 // FIXME: do this in a less traumatic way. We currently only allow
288 // locked apps in phone mode (see FIXME in Lockscreen component in
289 // this same file). When that changes, we need to do something
290 // nicer here. But this code is currently just to prevent a
291 // theoretical attack where user enters lockedApp mode, then makes
292 // the screen larger (maybe connects to monitor) and tries to enter
293 // tablet mode.
295 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
296 ? "phone"
297 : shell.usageScenario
298 readonly property string qmlComponent: {
299 if (applicationsDisplayLoader.usageScenario === "phone") {
300 return "Stages/PhoneStage.qml";
301 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
302 return "Stages/TabletStage.qml";
303 } else {
304 return "Stages/DesktopStage.qml";
305 }
306 }
307 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
308 // Currently the new one will get loaded while the old is still hanging
309 // around for a bit, which might lead to conflicts where both stages
310 // change the model simultaneously.
311 onQmlComponentChanged: {
312 if (item) item.stageAboutToBeUnloaded();
313 source = qmlComponent;
314 }
316 property bool interactive: (!greeter || !greeter.shown)
317 && panel.indicators.fullyClosed
318 && launcher.progress == 0
319 && !notifications.useModal
321 onInteractiveChanged: { if (interactive) { focus = true; } }
323 Binding {
324 target: applicationsDisplayLoader.item
325 property: "focus"
326 value: true
327 }
328 Binding {
329 target: applicationsDisplayLoader.item
330 property: "objectName"
331 value: "stage"
332 }
333 Binding {
334 target: applicationsDisplayLoader.item
335 property: "dragAreaWidth"
336 value: shell.edgeSize
337 }
338 Binding {
339 target: applicationsDisplayLoader.item
340 property: "maximizedAppTopMargin"
341 // Not just using panel.panelHeight as that changes depending on the focused app.
342 value: panel.indicators.minimizedPanelHeight
343 }
344 Binding {
345 target: applicationsDisplayLoader.item
346 property: "interactive"
347 value: applicationsDisplayLoader.interactive
348 }
349 Binding {
350 target: applicationsDisplayLoader.item
351 property: "spreadEnabled"
352 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
353 }
354 Binding {
355 target: applicationsDisplayLoader.item
356 property: "inverseProgress"
357 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
358 }
359 Binding {
360 target: applicationsDisplayLoader.item
361 property: "shellOrientationAngle"
362 value: shell.orientationAngle
363 }
364 Binding {
365 target: applicationsDisplayLoader.item
366 property: "shellOrientation"
367 value: shell.orientation
368 }
369 Binding {
370 target: applicationsDisplayLoader.item
371 property: "orientations"
372 value: shell.orientations
373 }
374 Binding {
375 target: applicationsDisplayLoader.item
376 property: "background"
377 value: wallpaperResolver.cachedBackground
378 }
379 Binding {
380 target: applicationsDisplayLoader.item
381 property: "nativeWidth"
382 value: shell.nativeWidth
383 }
384 Binding {
385 target: applicationsDisplayLoader.item
386 property: "nativeHeight"
387 value: shell.nativeHeight
388 }
389 Binding {
390 target: applicationsDisplayLoader.item
391 property: "beingResized"
392 value: shell.beingResized
393 }
394 Binding {
395 target: applicationsDisplayLoader.item
396 property: "keepDashRunning"
397 value: launcher.shown || launcher.dashSwipe
398 }
399 Binding {
400 target: applicationsDisplayLoader.item
401 property: "suspended"
402 value: greeter.shown
403 }
404 Binding {
405 target: applicationsDisplayLoader.item
406 property: "altTabPressed"
407 value: physicalKeysMapper.altTabPressed
408 }
409 Binding {
410 target: applicationsDisplayLoader.item
411 property: "leftMargin"
412 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
413 }
414 Binding {
415 target: applicationsDisplayLoader.item
416 property: "applicationManager"
417 value: ApplicationManager
418 }
419 Binding {
420 target: applicationsDisplayLoader.item
421 property: "topLevelSurfaceList"
422 value: topLevelSurfaceList
423 }
424 Binding {
425 target: applicationsDisplayLoader.item
426 property: "oskEnabled"
427 value: shell.oskEnabled
428 }
429 }
430 }
432 InputMethod {
433 id: inputMethod
434 objectName: "inputMethod"
435 anchors {
436 fill: parent
437 topMargin: panel.panelHeight
438 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
439 }
440 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
441 }
443 Loader {
444 id: greeterLoader
445 anchors.fill: parent
446 anchors.topMargin: panel.panelHeight
447 sourceComponent: shell.mode != "shell" ? integratedGreeter :
448 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
449 onLoaded: {
450 item.objectName = "greeter"
451 }
452 }
454 Component {
455 id: integratedGreeter
456 Greeter {
458 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
459 hides: [launcher, panel.indicators, panel.applicationMenus]
460 tabletMode: shell.usageScenario != "phone"
461 launcherOffset: launcher.progress
462 forcedUnlock: wizard.active || shell.mode === "full-shell"
463 background: wallpaperResolver.cachedBackground
464 hasCustomBackground: wallpaperResolver.hasCustomBackground
465 allowFingerprint: !dialogs.hasActiveDialog &&
466 !notifications.topmostIsFullscreen &&
467 !panel.indicators.shown
469 // avoid overlapping with Launcher's edge drag area
470 // FIXME: Fix TouchRegistry & friends and remove this workaround
471 // Issue involves launcher's DDA getting disabled on a long
472 // left-edge drag
473 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
475 onSessionStarted: {
476 launcher.hide();
477 }
479 onTease: {
480 if (!tutorial.running) {
481 launcher.tease();
482 }
483 }
485 onEmergencyCall: startLockedApp("dialer-app")
486 }
487 }
489 Timer {
490 // See powerConnection for why this is useful
491 id: showGreeterDelayed
492 interval: 1
493 onTriggered: {
494 greeter.forceShow();
495 }
496 }
498 Connections {
499 id: callConnection
500 target: callManager
502 onHasCallsChanged: {
503 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
504 // We just received an incoming call while locked. The
505 // indicator will have already launched dialer-app for us, but
506 // there is a race between "hasCalls" changing and the dialer
507 // starting up. So in case we lose that race, we'll start/
508 // focus the dialer ourselves here too. Even if the indicator
509 // didn't launch the dialer for some reason (or maybe a call
510 // started via some other means), if an active call is
511 // happening, we want to be in the dialer.
512 startLockedApp("dialer-app")
513 }
514 }
515 }
517 Connections {
518 id: powerConnection
519 target: Powerd
521 onStatusChanged: {
522 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
523 !callManager.hasCalls && !wizard.active) {
524 // We don't want to simply call greeter.showNow() here, because
525 // that will take too long. Qt will delay button event
526 // handling until the greeter is done loading and may think the
527 // user held down the power button the whole time, leading to a
528 // power dialog being shown. Instead, delay showing the
529 // greeter until we've finished handling the event. We could
530 // make the greeter load asynchronously instead, but that
531 // introduces a whole host of timing issues, especially with
532 // its animations. So this is simpler.
533 showGreeterDelayed.start();
534 }
535 }
536 }
538 function showHome() {
539 greeter.notifyUserRequestedApp();
541 if (shell.mode === "greeter") {
542 SessionBroadcast.requestHomeShown(AccountsService.user);
543 } else {
544 var animate = !LightDMService.greeter.active && !stages.shown;
545 dash.setCurrentScope(0, animate, false);
546 ApplicationManager.requestFocusApplication("unity8-dash");
547 }
548 }
550 function showDash() {
551 if (greeter.notifyShowingDashFromDrag()) {
552 launcher.fadeOut();
553 }
555 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
556 && ApplicationManager.focusedApplicationId != "unity8-dash") {
557 ApplicationManager.requestFocusApplication("unity8-dash")
558 launcher.fadeOut();
559 }
560 }
562 Item {
563 id: overlay
564 z: 10
566 anchors.fill: parent
568 Panel {
569 id: panel
570 objectName: "panel"
571 anchors.fill: parent //because this draws indicator menus
573 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
574 minimizedPanelHeight: units.gu(3)
575 expandedPanelHeight: units.gu(7)
576 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
577 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
579 indicators {
580 hides: [launcher]
581 available: tutorial.panelEnabled
582 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
583 && (!greeter || !greeter.hasLockedApp)
584 && !shell.waitingOnGreeter
586 model: Indicators.IndicatorsModel {
587 // tablet and phone both use the same profile
588 // FIXME: use just "phone" for greeter too, but first fix
589 // greeter app launching to either load the app inside the
590 // greeter or tell the session to load the app. This will
591 // involve taking the url-dispatcher dbus name and using
592 // SessionBroadcast to tell the session.
593 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
594 Component.onCompleted: load();
595 }
596 }
598 applicationMenus {
599 hides: [launcher]
600 }
602 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
603 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
604 : false
605 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
606 || greeter.hasLockedApp
607 locked: greeter && greeter.active
608 greeterShown: greeter && greeter.shown
609 }
611 Launcher {
612 id: launcher
613 objectName: "launcher"
615 /*
616 * Since the Dash doesn't have the same controll over surfaces that the
617 * Shell does, it can't slowly move the scope out of the way, as the shell
618 * does with apps, and the dash is show instantly. This allows for some
619 * leeway and prevents accidental home swipes.
620 */
621 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
622 readonly property bool dashSwipe: progress > offset
624 anchors.top: parent.top
625 anchors.topMargin: inverted ? 0 : panel.panelHeight
626 anchors.bottom: parent.bottom
627 width: parent.width
628 dragAreaWidth: shell.edgeSize
629 available: tutorial.launcherEnabled
630 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
631 && !greeter.hasLockedApp
632 && !shell.waitingOnGreeter
633 inverted: shell.usageScenario !== "desktop"
634 superPressed: physicalKeysMapper.superPressed
635 superTabPressed: physicalKeysMapper.superTabPressed
636 panelWidth: units.gu(settings.launcherWidth)
637 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
639 onShowDashHome: showHome()
640 onDash: showDash()
641 onDashSwipeChanged: {
642 if (dashSwipe) {
643 dash.setCurrentScope(0, false, true)
644 }
645 }
646 onLauncherApplicationSelected: {
647 greeter.notifyUserRequestedApp();
648 shell.activateApplication(appId);
649 }
650 onShownChanged: {
651 if (shown) {
652 panel.indicators.hide()
653 }
654 }
655 onFocusChanged: {
656 if (!focus) {
657 applicationsDisplayLoader.focus = true;
658 }
659 }
661 GlobalShortcut {
662 shortcut: Qt.AltModifier | Qt.Key_F1
663 onTriggered: {
664 launcher.openForKeyboardNavigation();
665 }
666 }
667 GlobalShortcut {
668 shortcut: Qt.MetaModifier | Qt.Key_0
669 onTriggered: {
670 if (LauncherModel.get(9)) {
671 activateApplication(LauncherModel.get(9).appId);
672 }
673 }
674 }
675 Repeater {
676 model: 9
677 GlobalShortcut {
678 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
679 onTriggered: {
680 if (LauncherModel.get(index)) {
681 activateApplication(LauncherModel.get(index).appId);
682 }
683 }
684 }
685 }
686 }
688 KeyboardShortcutsOverlay {
689 objectName: "shortcutsOverlay"
690 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
691 && height < parent.height - padding - panel.panelHeight
692 anchors.centerIn: parent
693 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
694 anchors.verticalCenterOffset: panel.panelHeight/2
695 visible: opacity > 0
696 opacity: enabled ? 0.95 : 0
698 Behavior on opacity {
699 UbuntuNumberAnimation {}
700 }
701 }
703 Tutorial {
704 id: tutorial
705 objectName: "tutorial"
706 anchors.fill: parent
708 paused: callManager.hasCalls || !greeter || greeter.active ||
709 wizard.active
710 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
711 inputMethod.visible ||
712 (launcher.shown && !launcher.lockedVisible) ||
713 panel.indicators.shown || stage.dragProgress > 0
714 usageScenario: shell.usageScenario
715 lastInputTimestamp: inputFilter.lastInputTimestamp
716 launcher: launcher
717 panel: panel
718 stage: applicationsDisplayLoader.item
719 }
721 Wizard {
722 id: wizard
723 objectName: "wizard"
724 anchors.fill: parent
725 deferred: shell.mode === "greeter"
727 function unlockWhenDoneWithWizard() {
728 if (!active) {
729 Connectivity.unlockAllModems();
730 }
731 }
733 Component.onCompleted: unlockWhenDoneWithWizard()
734 onActiveChanged: unlockWhenDoneWithWizard()
735 }
737 MouseArea { // modal notifications prevent interacting with other contents
738 anchors.fill: parent
739 visible: notifications.useModal
740 enabled: visible
741 }
743 Notifications {
744 id: notifications
746 model: NotificationBackend.Model
747 margin: units.gu(1)
748 hasMouse: shell.hasMouse
749 background: wallpaperResolver.cachedBackground
751 y: topmostIsFullscreen ? 0 : panel.panelHeight
752 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
754 states: [
755 State {
756 name: "narrow"
757 when: overlay.width <= units.gu(60)
758 AnchorChanges {
759 target: notifications
760 anchors.left: parent.left
761 anchors.right: parent.right
762 }
763 },
764 State {
765 name: "wide"
766 when: overlay.width > units.gu(60)
767 AnchorChanges {
768 target: notifications
769 anchors.left: undefined
770 anchors.right: parent.right
771 }
772 PropertyChanges { target: notifications; width: units.gu(38) }
773 }
774 ]
775 }
776 }
778 Dialogs {
779 id: dialogs
780 objectName: "dialogs"
781 anchors.fill: parent
782 z: overlay.z + 10
783 usageScenario: shell.usageScenario
784 onPowerOffClicked: {
785 shutdownFadeOutRectangle.enabled = true;
786 shutdownFadeOutRectangle.visible = true;
787 shutdownFadeOut.start();
788 }
789 }
791 Connections {
792 target: SessionBroadcast
793 onShowHome: if (shell.mode !== "greeter") showHome()
794 }
796 URLDispatcher {
797 id: urlDispatcher
798 objectName: "urlDispatcher"
799 active: shell.mode === "greeter"
800 onUrlRequested: shell.activateURL(url)
801 }
803 ItemGrabber {
804 id: itemGrabber
805 anchors.fill: parent
806 z: dialogs.z + 10
807 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
808 Connections {
809 target: applicationsDisplayLoader.item
810 ignoreUnknownSignals: true
811 onItemSnapshotRequested: itemGrabber.capture(item)
812 }
813 }
815 Cursor {
816 id: cursor
817 visible: shell.hasMouse
818 z: itemGrabber.z + 1
819 topBoundaryOffset: panel.panelHeight
821 confiningItem: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.itemConfiningMouseCursor : null
823 property bool mouseNeverMoved: true
824 Binding {
825 target: cursor; property: "x"; value: shell.width / 2
826 when: cursor.mouseNeverMoved && cursor.visible
827 }
828 Binding {
829 target: cursor; property: "y"; value: shell.height / 2
830 when: cursor.mouseNeverMoved && cursor.visible
831 }
833 height: units.gu(3)
835 readonly property var previewRectangle: applicationsDisplayLoader.item && applicationsDisplayLoader.item.previewRectangle &&
836 applicationsDisplayLoader.item.previewRectangle.target &&
837 applicationsDisplayLoader.item.previewRectangle.target.dragging ?
838 applicationsDisplayLoader.item.previewRectangle : null
840 onPushedLeftBoundary: {
841 if (buttons === Qt.NoButton) {
842 launcher.pushEdge(amount);
843 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
844 previewRectangle.maximizeLeft(amount);
845 }
846 }
848 onPushedRightBoundary: {
849 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
850 && applicationsDisplayLoader.item.pushRightEdge) {
851 applicationsDisplayLoader.item.pushRightEdge(amount);
852 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
853 previewRectangle.maximizeRight(amount);
854 }
855 }
857 onPushedTopBoundary: {
858 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
859 previewRectangle.maximize(amount);
860 }
861 }
862 onPushedTopLeftCorner: {
863 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
864 previewRectangle.maximizeTopLeft(amount);
865 }
866 }
867 onPushedTopRightCorner: {
868 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
869 previewRectangle.maximizeTopRight(amount);
870 }
871 }
872 onPushedBottomLeftCorner: {
873 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
874 previewRectangle.maximizeBottomLeft(amount);
875 }
876 }
877 onPushedBottomRightCorner: {
878 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
879 previewRectangle.maximizeBottomRight(amount);
880 }
881 }
882 onPushStopped: {
883 if (previewRectangle) {
884 previewRectangle.stop();
885 }
886 }
888 onMouseMoved: {
889 mouseNeverMoved = false;
890 cursor.opacity = 1;
891 }
892 }
894 // non-visual object
895 KeymapSwitcher {}
897 Rectangle {
898 id: shutdownFadeOutRectangle
899 z: cursor.z + 1
900 enabled: false
901 visible: false
902 color: "black"
903 anchors.fill: parent
904 opacity: 0.0
905 NumberAnimation on opacity {
906 id: shutdownFadeOut
907 from: 0.0
908 to: 1.0
909 onStopped: {
910 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
911 DBusUnitySessionService.shutdown();
912 }
913 }
914 }
915 }