on 3a39491 help sign in
unity8/qml/Shell.qml
912 lines 33 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]
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
572 indicators {
573 hides: [launcher]
574 available: tutorial.panelEnabled
575 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
576 && (!greeter || !greeter.hasLockedApp)
577 && !shell.waitingOnGreeter
578 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
580 minimizedPanelHeight: units.gu(3)
581 expandedPanelHeight: units.gu(7)
583 indicatorsModel: Indicators.IndicatorsModel {
584 // tablet and phone both use the same profile
585 // FIXME: use just "phone" for greeter too, but first fix
586 // greeter app launching to either load the app inside the
587 // greeter or tell the session to load the app. This will
588 // involve taking the url-dispatcher dbus name and using
589 // SessionBroadcast to tell the session.
590 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
591 Component.onCompleted: load();
592 }
593 }
595 callHint {
596 greeterShown: greeter.shown
597 }
599 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
600 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
601 : false
602 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
603 || greeter.hasLockedApp
604 locked: greeter && greeter.active
605 }
607 Launcher {
608 id: launcher
609 objectName: "launcher"
611 /*
612 * Since the Dash doesn't have the same controll over surfaces that the
613 * Shell does, it can't slowly move the scope out of the way, as the shell
614 * does with apps, and the dash is show instantly. This allows for some
615 * leeway and prevents accidental home swipes.
616 */
617 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
618 readonly property bool dashSwipe: progress > offset
620 anchors.top: parent.top
621 anchors.topMargin: inverted ? 0 : panel.panelHeight
622 anchors.bottom: parent.bottom
623 width: parent.width
624 dragAreaWidth: shell.edgeSize
625 available: tutorial.launcherEnabled
626 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
627 && !greeter.hasLockedApp
628 && !shell.waitingOnGreeter
629 inverted: shell.usageScenario !== "desktop"
630 superPressed: physicalKeysMapper.superPressed
631 superTabPressed: physicalKeysMapper.superTabPressed
632 panelWidth: units.gu(settings.launcherWidth)
633 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
635 onShowDashHome: showHome()
636 onDash: showDash()
637 onDashSwipeChanged: {
638 if (dashSwipe) {
639 dash.setCurrentScope(0, false, true)
640 }
641 }
642 onLauncherApplicationSelected: {
643 greeter.notifyUserRequestedApp();
644 shell.activateApplication(appId);
645 }
646 onShownChanged: {
647 if (shown) {
648 panel.indicators.hide()
649 }
650 }
651 onFocusChanged: {
652 if (!focus) {
653 applicationsDisplayLoader.focus = true;
654 }
655 }
657 GlobalShortcut {
658 shortcut: Qt.AltModifier | Qt.Key_F1
659 onTriggered: {
660 launcher.openForKeyboardNavigation();
661 }
662 }
663 GlobalShortcut {
664 shortcut: Qt.MetaModifier | Qt.Key_0
665 onTriggered: {
666 if (LauncherModel.get(9)) {
667 activateApplication(LauncherModel.get(9).appId);
668 }
669 }
670 }
671 Repeater {
672 model: 9
673 GlobalShortcut {
674 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
675 onTriggered: {
676 if (LauncherModel.get(index)) {
677 activateApplication(LauncherModel.get(index).appId);
678 }
679 }
680 }
681 }
682 }
684 KeyboardShortcutsOverlay {
685 objectName: "shortcutsOverlay"
686 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
687 && height < parent.height - padding - panel.panelHeight
688 anchors.centerIn: parent
689 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
690 anchors.verticalCenterOffset: panel.panelHeight/2
691 visible: opacity > 0
692 opacity: enabled ? 0.95 : 0
694 Behavior on opacity {
695 UbuntuNumberAnimation {}
696 }
697 }
699 Tutorial {
700 id: tutorial
701 objectName: "tutorial"
702 anchors.fill: parent
704 paused: callManager.hasCalls || !greeter || greeter.active ||
705 wizard.active
706 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
707 inputMethod.visible ||
708 (launcher.shown && !launcher.lockedVisible) ||
709 panel.indicators.shown || stage.dragProgress > 0
710 usageScenario: shell.usageScenario
711 lastInputTimestamp: inputFilter.lastInputTimestamp
712 launcher: launcher
713 panel: panel
714 stage: applicationsDisplayLoader.item
715 }
717 Wizard {
718 id: wizard
719 objectName: "wizard"
720 anchors.fill: parent
721 deferred: shell.mode === "greeter"
723 function unlockWhenDoneWithWizard() {
724 if (!active) {
725 Connectivity.unlockAllModems();
726 }
727 }
729 Component.onCompleted: unlockWhenDoneWithWizard()
730 onActiveChanged: unlockWhenDoneWithWizard()
731 }
733 MouseArea { // modal notifications prevent interacting with other contents
734 anchors.fill: parent
735 visible: notifications.useModal
736 enabled: visible
737 }
739 Notifications {
740 id: notifications
742 model: NotificationBackend.Model
743 margin: units.gu(1)
744 hasMouse: shell.hasMouse
745 background: wallpaperResolver.cachedBackground
747 y: topmostIsFullscreen ? 0 : panel.panelHeight
748 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
750 states: [
751 State {
752 name: "narrow"
753 when: overlay.width <= units.gu(60)
754 AnchorChanges {
755 target: notifications
756 anchors.left: parent.left
757 anchors.right: parent.right
758 }
759 },
760 State {
761 name: "wide"
762 when: overlay.width > units.gu(60)
763 AnchorChanges {
764 target: notifications
765 anchors.left: undefined
766 anchors.right: parent.right
767 }
768 PropertyChanges { target: notifications; width: units.gu(38) }
769 }
770 ]
771 }
772 }
774 Dialogs {
775 id: dialogs
776 objectName: "dialogs"
777 anchors.fill: parent
778 z: overlay.z + 10
779 usageScenario: shell.usageScenario
780 onPowerOffClicked: {
781 shutdownFadeOutRectangle.enabled = true;
782 shutdownFadeOutRectangle.visible = true;
783 shutdownFadeOut.start();
784 }
785 }
787 Connections {
788 target: SessionBroadcast
789 onShowHome: if (shell.mode !== "greeter") showHome()
790 }
792 URLDispatcher {
793 id: urlDispatcher
794 objectName: "urlDispatcher"
795 active: shell.mode === "greeter"
796 onUrlRequested: shell.activateURL(url)
797 }
799 ItemGrabber {
800 id: itemGrabber
801 anchors.fill: parent
802 z: dialogs.z + 10
803 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
804 Connections {
805 target: applicationsDisplayLoader.item
806 ignoreUnknownSignals: true
807 onItemSnapshotRequested: itemGrabber.capture(item)
808 }
809 }
811 Cursor {
812 id: cursor
813 visible: shell.hasMouse
814 z: itemGrabber.z + 1
815 topBoundaryOffset: panel.panelHeight
817 confiningItem: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.itemConfiningMouseCursor : null
819 property bool mouseNeverMoved: true
820 Binding {
821 target: cursor; property: "x"; value: shell.width / 2
822 when: cursor.mouseNeverMoved && cursor.visible
823 }
824 Binding {
825 target: cursor; property: "y"; value: shell.height / 2
826 when: cursor.mouseNeverMoved && cursor.visible
827 }
829 height: units.gu(3)
831 readonly property var previewRectangle: applicationsDisplayLoader.item && applicationsDisplayLoader.item.previewRectangle &&
832 applicationsDisplayLoader.item.previewRectangle.target &&
833 applicationsDisplayLoader.item.previewRectangle.target.dragging ?
834 applicationsDisplayLoader.item.previewRectangle : null
836 onPushedLeftBoundary: {
837 if (buttons === Qt.NoButton) {
838 launcher.pushEdge(amount);
839 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
840 previewRectangle.maximizeLeft(amount);
841 }
842 }
844 onPushedRightBoundary: {
845 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
846 && applicationsDisplayLoader.item.pushRightEdge) {
847 applicationsDisplayLoader.item.pushRightEdge(amount);
848 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
849 previewRectangle.maximizeRight(amount);
850 }
851 }
853 onPushedTopBoundary: {
854 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
855 previewRectangle.maximize(amount);
856 }
857 }
858 onPushedTopLeftCorner: {
859 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
860 previewRectangle.maximizeTopLeft(amount);
861 }
862 }
863 onPushedTopRightCorner: {
864 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
865 previewRectangle.maximizeTopRight(amount);
866 }
867 }
868 onPushedBottomLeftCorner: {
869 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
870 previewRectangle.maximizeBottomLeft(amount);
871 }
872 }
873 onPushedBottomRightCorner: {
874 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
875 previewRectangle.maximizeBottomRight(amount);
876 }
877 }
878 onPushStopped: {
879 if (previewRectangle) {
880 previewRectangle.stop();
881 }
882 }
884 onMouseMoved: {
885 mouseNeverMoved = false;
886 cursor.opacity = 1;
887 }
888 }
890 // non-visual object
891 KeymapSwitcher {}
893 Rectangle {
894 id: shutdownFadeOutRectangle
895 z: cursor.z + 1
896 enabled: false
897 visible: false
898 color: "black"
899 anchors.fill: parent
900 opacity: 0.0
901 NumberAnimation on opacity {
902 id: shutdownFadeOut
903 from: 0.0
904 to: 1.0
905 onStopped: {
906 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
907 DBusUnitySessionService.shutdown();
908 }
909 }
910 }
911 }