on 5ca7d98 help sign in
unity8/qml/Shell.qml
803 lines 29 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 "Stage"
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 string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
62 property string mode: "full-greeter"
63 property alias oskEnabled: inputMethod.enabled
64 function updateFocusedAppOrientation() {
65 stage.updateFocusedAppOrientation();
66 }
67 function updateFocusedAppOrientationAnimated() {
68 stage.updateFocusedAppOrientationAnimated();
69 }
70 property bool hasMouse: false
72 // to be read from outside
73 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
75 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
76 && stage.orientationChangesEnabled
77 && (!greeter || !greeter.animating)
79 readonly property bool showingGreeter: greeter && greeter.shown
81 property bool startingUp: true
82 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
84 property int supportedOrientations: {
85 if (startingUp) {
86 // Ensure we don't rotate during start up
87 return Qt.PrimaryOrientation;
88 } else if (showingGreeter || notifications.topmostIsFullscreen) {
89 return Qt.PrimaryOrientation;
90 } else {
91 return shell.orientations.map(stage.supportedOrientations);
92 }
93 }
95 readonly property var mainApp: stage.mainApp
97 onMainAppChanged: {
98 if (mainApp) {
99 _onMainAppChanged(mainApp.appId);
100 }
101 }
102 Connections {
103 target: ApplicationManager
104 onFocusRequested: {
105 if (shell.mainApp && shell.mainApp.appId === appId) {
106 _onMainAppChanged(appId);
107 }
108 }
109 }
110 function _onMainAppChanged(appId) {
111 if (wizard.active && appId != "" && appId != "unity8-dash") {
112 // If this happens on first boot, we may be in the
113 // wizard while receiving a call. But a call is more
114 // important than the wizard so just bail out of it.
115 wizard.hide();
116 }
118 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
119 // If we are in the middle of a call, make dialer lockedApp and show it.
120 // This can happen if user backs out of dialer back to greeter, then
121 // launches dialer again.
122 greeter.lockedApp = appId;
123 }
124 greeter.notifyAppFocusRequested(appId);
126 panel.indicators.hide();
127 launcher.hide();
128 }
130 // For autopilot consumption
131 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
133 // Note when greeter is waiting on PAM, so that we can disable edges until
134 // we know which user data to show and whether the session is locked.
135 readonly property bool waitingOnGreeter: greeter && greeter.waiting
137 property real edgeSize: units.gu(settings.edgeDragWidth)
139 WallpaperResolver {
140 id: wallpaperResolver
141 objectName: "wallpaperResolver"
143 readonly property url defaultBackground: "file:///usr/share/backgrounds/warty-final-ubuntu.png"
144 readonly property bool hasCustomBackground: background != defaultBackground
146 // Use a cached version of the scaled-down wallpaper (as sometimes the
147 // image can be quite big compared to the device size, including for
148 // our default wallpaper). We use a name=wallpaper argument here to
149 // make sure we don't litter our cache with lots of scaled images. We
150 // only need to bother caching one at a time.
151 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
153 GSettings {
154 id: backgroundSettings
155 schema.id: "org.gnome.desktop.background"
156 }
158 candidates: [
159 AccountsService.backgroundFile,
160 backgroundSettings.pictureUri,
161 defaultBackground
162 ]
163 }
165 readonly property alias greeter: greeterLoader.item
167 function activateApplication(appId) {
168 // Either open the app in our own session, or -- if we're acting as a
169 // greeter -- ask the user's session to open it for us.
170 if (shell.mode === "greeter") {
171 activateURL("application:///" + appId + ".desktop");
172 } else {
173 startApp(appId);
174 }
175 }
177 function activateURL(url) {
178 SessionBroadcast.requestUrlStart(AccountsService.user, url);
179 greeter.notifyUserRequestedApp();
180 panel.indicators.hide();
181 }
183 function startApp(appId) {
184 if (ApplicationManager.findApplication(appId)) {
185 ApplicationManager.requestFocusApplication(appId);
186 } else {
187 ApplicationManager.startApplication(appId);
188 }
189 }
191 function startLockedApp(app) {
192 if (greeter.locked) {
193 greeter.lockedApp = app;
194 }
195 startApp(app); // locked apps are always in our same session
196 }
198 Binding {
199 target: LauncherModel
200 property: "applicationManager"
201 value: ApplicationManager
202 }
204 Component.onCompleted: {
205 finishStartUpTimer.start();
206 }
208 VolumeControl {
209 id: volumeControl
210 }
212 DashCommunicator {
213 id: dash
214 objectName: "dashCommunicator"
215 }
217 PhysicalKeysMapper {
218 id: physicalKeysMapper
219 objectName: "physicalKeysMapper"
221 onPowerKeyLongPressed: dialogs.showPowerDialog();
222 onVolumeDownTriggered: volumeControl.volumeDown();
223 onVolumeUpTriggered: volumeControl.volumeUp();
224 onScreenshotTriggered: itemGrabber.capture(shell);
225 }
227 GlobalShortcut {
228 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
229 }
231 WindowInputFilter {
232 id: inputFilter
233 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
234 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
235 }
237 WindowInputMonitor {
238 objectName: "windowInputMonitor"
239 onHomeKeyActivated: {
240 // Ignore when greeter is active, to avoid pocket presses
241 if (!greeter.active) {
242 launcher.fadeOut();
243 shell.showHome();
244 }
245 }
246 onTouchBegun: { cursor.opacity = 0; }
247 onTouchEnded: {
248 // move the (hidden) cursor to the last known touch position
249 var mappedCoords = mapFromItem(null, pos.x, pos.y);
250 cursor.x = mappedCoords.x;
251 cursor.y = mappedCoords.y;
252 cursor.mouseNeverMoved = false;
253 }
254 }
256 GSettings {
257 id: settings
258 schema.id: "com.canonical.Unity8"
259 }
261 Item {
262 id: stages
263 objectName: "stages"
264 width: parent.width
265 height: parent.height
267 TopLevelSurfaceList {
268 id: topLevelSurfaceList
269 objectName: "topLevelSurfaceList"
270 applicationsModel: ApplicationManager
271 }
273 Stage {
274 id: stage
275 objectName: "stage"
276 anchors.fill: parent
277 focus: true
279 dragAreaWidth: shell.edgeSize
280 background: wallpaperResolver.background
281 leftEdgeDragProgress: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 :
282 Math.max(0, (launcher.dragDistance * (stage.width - launcher.panelWidth) / stage.width) - launcher.panelWidth)
284 applicationManager: ApplicationManager
285 topLevelSurfaceList: topLevelSurfaceList
286 inputMethodRect: inputMethod.visibleRect
288 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
289 ? "phone"
290 : shell.usageScenario
292 mode: usageScenario == "phone" ? "staged"
293 : usageScenario == "tablet" ? "stagedWithSideStage"
294 : "windowed"
296 shellOrientation: shell.orientation
297 shellOrientationAngle: shell.orientationAngle
298 orientations: shell.orientations
299 nativeWidth: shell.nativeWidth
300 nativeHeight: shell.nativeHeight
302 // TODO: Is this still needed? Didn't come across it. Needs checking before merging
303 // Not just using panel.panelHeight as that changes depending on the focused app.
304 maximizedAppTopMargin: panel.indicators.minimizedPanelHeight
306 interactive: (!greeter || !greeter.shown)
307 && panel.indicators.fullyClosed
308 && launcher.progress == 0
309 && !notifications.useModal
311 onInteractiveChanged: { if (interactive) { focus = true; } }
313 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
314 suspended: greeter.shown
315 keepDashRunning: launcher.shown || launcher.dashSwipe
316 altTabPressed: physicalKeysMapper.altTabPressed
317 oskEnabled: shell.oskEnabled
319 // TODO: This is not implemented yet in the new stage...
320 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
321 }
322 }
324 InputMethod {
325 id: inputMethod
326 objectName: "inputMethod"
327 anchors {
328 fill: parent
329 topMargin: panel.panelHeight
330 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
331 }
332 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
333 }
335 Loader {
336 id: greeterLoader
337 objectName: "greeterLoader"
338 anchors.fill: parent
339 anchors.topMargin: panel.panelHeight
340 sourceComponent: shell.mode != "shell" ? integratedGreeter :
341 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
342 onLoaded: {
343 item.objectName = "greeter"
344 }
345 }
347 Component {
348 id: integratedGreeter
349 Greeter {
351 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
352 hides: [launcher, panel.indicators]
353 tabletMode: shell.usageScenario != "phone"
354 launcherOffset: launcher.progress
355 forcedUnlock: wizard.active || shell.mode === "full-shell"
356 background: wallpaperResolver.cachedBackground
357 hasCustomBackground: wallpaperResolver.hasCustomBackground
358 allowFingerprint: !dialogs.hasActiveDialog &&
359 !notifications.topmostIsFullscreen &&
360 !panel.indicators.shown
362 // avoid overlapping with Launcher's edge drag area
363 // FIXME: Fix TouchRegistry & friends and remove this workaround
364 // Issue involves launcher's DDA getting disabled on a long
365 // left-edge drag
366 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
368 onSessionStarted: {
369 launcher.hide();
370 }
372 onTease: {
373 if (!tutorial.running) {
374 launcher.tease();
375 }
376 }
378 onEmergencyCall: startLockedApp("dialer-app")
379 }
380 }
382 Timer {
383 // See powerConnection for why this is useful
384 id: showGreeterDelayed
385 interval: 1
386 onTriggered: {
387 greeter.forceShow();
388 }
389 }
391 Connections {
392 id: callConnection
393 target: callManager
395 onHasCallsChanged: {
396 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
397 // We just received an incoming call while locked. The
398 // indicator will have already launched dialer-app for us, but
399 // there is a race between "hasCalls" changing and the dialer
400 // starting up. So in case we lose that race, we'll start/
401 // focus the dialer ourselves here too. Even if the indicator
402 // didn't launch the dialer for some reason (or maybe a call
403 // started via some other means), if an active call is
404 // happening, we want to be in the dialer.
405 startLockedApp("dialer-app")
406 }
407 }
408 }
410 Connections {
411 id: powerConnection
412 target: Powerd
414 onStatusChanged: {
415 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
416 !callManager.hasCalls && !wizard.active) {
417 // We don't want to simply call greeter.showNow() here, because
418 // that will take too long. Qt will delay button event
419 // handling until the greeter is done loading and may think the
420 // user held down the power button the whole time, leading to a
421 // power dialog being shown. Instead, delay showing the
422 // greeter until we've finished handling the event. We could
423 // make the greeter load asynchronously instead, but that
424 // introduces a whole host of timing issues, especially with
425 // its animations. So this is simpler.
426 showGreeterDelayed.start();
427 }
428 }
429 }
431 function showHome() {
432 greeter.notifyUserRequestedApp();
434 if (shell.mode === "greeter") {
435 SessionBroadcast.requestHomeShown(AccountsService.user);
436 } else {
437 var animate = !LightDMService.greeter.active && !stages.shown;
438 dash.setCurrentScope(0, animate, false);
439 ApplicationManager.requestFocusApplication("unity8-dash");
440 }
441 }
443 function showDash() {
444 if (greeter.notifyShowingDashFromDrag()) {
445 launcher.fadeOut();
446 }
448 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
449 && ApplicationManager.focusedApplicationId != "unity8-dash") {
450 ApplicationManager.requestFocusApplication("unity8-dash")
451 launcher.fadeOut();
452 }
453 }
455 Item {
456 id: overlay
457 z: 10
459 anchors.fill: parent
461 Panel {
462 id: panel
463 objectName: "panel"
464 anchors.fill: parent //because this draws indicator menus
465 indicators {
466 hides: [launcher]
467 available: tutorial.panelEnabled
468 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
469 && (!greeter || !greeter.hasLockedApp)
470 && !shell.waitingOnGreeter
471 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
473 minimizedPanelHeight: units.gu(3)
474 expandedPanelHeight: units.gu(7)
476 indicatorsModel: Indicators.IndicatorsModel {
477 // tablet and phone both use the same profile
478 // FIXME: use just "phone" for greeter too, but first fix
479 // greeter app launching to either load the app inside the
480 // greeter or tell the session to load the app. This will
481 // involve taking the url-dispatcher dbus name and using
482 // SessionBroadcast to tell the session.
483 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
484 Component.onCompleted: load();
485 }
486 }
488 callHint {
489 greeterShown: greeter.shown
490 }
492 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
493 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
494 : false
495 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
496 || greeter.hasLockedApp
497 locked: greeter && greeter.active
498 }
500 Launcher {
501 id: launcher
502 objectName: "launcher"
504 /*
505 * Since the Dash doesn't have the same controll over surfaces that the
506 * Shell does, it can't slowly move the scope out of the way, as the shell
507 * does with apps, and the dash is show instantly. This allows for some
508 * leeway and prevents accidental home swipes.
509 */
510 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
511 readonly property bool dashSwipe: progress > offset
513 anchors.top: parent.top
514 anchors.topMargin: inverted ? 0 : panel.panelHeight
515 anchors.bottom: parent.bottom
516 width: parent.width
517 dragAreaWidth: shell.edgeSize
518 available: tutorial.launcherEnabled
519 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
520 && !greeter.hasLockedApp
521 && !shell.waitingOnGreeter
522 inverted: shell.usageScenario !== "desktop"
523 superPressed: physicalKeysMapper.superPressed
524 superTabPressed: physicalKeysMapper.superTabPressed
525 panelWidth: units.gu(settings.launcherWidth)
526 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
528 onShowDashHome: showHome()
529 onDash: showDash()
530 onDashSwipeChanged: {
531 if (dashSwipe) {
532 dash.setCurrentScope(0, false, true)
533 }
534 }
535 onLauncherApplicationSelected: {
536 greeter.notifyUserRequestedApp();
537 shell.activateApplication(appId);
538 }
539 onShownChanged: {
540 if (shown) {
541 panel.indicators.hide()
542 }
543 }
544 onFocusChanged: {
545 if (!focus) {
546 stage.focus = true;
547 }
548 }
550 GlobalShortcut {
551 shortcut: Qt.AltModifier | Qt.Key_F1
552 onTriggered: {
553 launcher.openForKeyboardNavigation();
554 }
555 }
556 GlobalShortcut {
557 shortcut: Qt.MetaModifier | Qt.Key_0
558 onTriggered: {
559 if (LauncherModel.get(9)) {
560 activateApplication(LauncherModel.get(9).appId);
561 }
562 }
563 }
564 Repeater {
565 model: 9
566 GlobalShortcut {
567 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
568 onTriggered: {
569 if (LauncherModel.get(index)) {
570 activateApplication(LauncherModel.get(index).appId);
571 }
572 }
573 }
574 }
575 }
577 KeyboardShortcutsOverlay {
578 objectName: "shortcutsOverlay"
579 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
580 && height < parent.height - padding - panel.panelHeight
581 anchors.centerIn: parent
582 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
583 anchors.verticalCenterOffset: panel.panelHeight/2
584 visible: opacity > 0
585 opacity: enabled ? 0.95 : 0
587 Behavior on opacity {
588 UbuntuNumberAnimation {}
589 }
590 }
592 Tutorial {
593 id: tutorial
594 objectName: "tutorial"
595 anchors.fill: parent
597 paused: callManager.hasCalls || !greeter || greeter.active ||
598 wizard.active
599 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
600 inputMethod.visible ||
601 (launcher.shown && !launcher.lockedVisible) ||
602 panel.indicators.shown || stage.rightEdgeDragProgress > 0
603 usageScenario: shell.usageScenario
604 lastInputTimestamp: inputFilter.lastInputTimestamp
605 launcher: launcher
606 panel: panel
607 stage: stage
608 }
610 Wizard {
611 id: wizard
612 objectName: "wizard"
613 anchors.fill: parent
614 deferred: shell.mode === "greeter"
616 function unlockWhenDoneWithWizard() {
617 if (!active) {
618 Connectivity.unlockAllModems();
619 }
620 }
622 Component.onCompleted: unlockWhenDoneWithWizard()
623 onActiveChanged: unlockWhenDoneWithWizard()
624 }
626 MouseArea { // modal notifications prevent interacting with other contents
627 anchors.fill: parent
628 visible: notifications.useModal
629 enabled: visible
630 }
632 Notifications {
633 id: notifications
635 model: NotificationBackend.Model
636 margin: units.gu(1)
637 hasMouse: shell.hasMouse
638 background: wallpaperResolver.cachedBackground
640 y: topmostIsFullscreen ? 0 : panel.panelHeight
641 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
643 states: [
644 State {
645 name: "narrow"
646 when: overlay.width <= units.gu(60)
647 AnchorChanges {
648 target: notifications
649 anchors.left: parent.left
650 anchors.right: parent.right
651 }
652 },
653 State {
654 name: "wide"
655 when: overlay.width > units.gu(60)
656 AnchorChanges {
657 target: notifications
658 anchors.left: undefined
659 anchors.right: parent.right
660 }
661 PropertyChanges { target: notifications; width: units.gu(38) }
662 }
663 ]
664 }
665 }
667 Dialogs {
668 id: dialogs
669 objectName: "dialogs"
670 anchors.fill: parent
671 z: overlay.z + 10
672 usageScenario: shell.usageScenario
673 onPowerOffClicked: {
674 shutdownFadeOutRectangle.enabled = true;
675 shutdownFadeOutRectangle.visible = true;
676 shutdownFadeOut.start();
677 }
678 }
680 Connections {
681 target: SessionBroadcast
682 onShowHome: if (shell.mode !== "greeter") showHome()
683 }
685 URLDispatcher {
686 id: urlDispatcher
687 objectName: "urlDispatcher"
688 active: shell.mode === "greeter"
689 onUrlRequested: shell.activateURL(url)
690 }
692 ItemGrabber {
693 id: itemGrabber
694 anchors.fill: parent
695 z: dialogs.z + 10
696 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
697 Connections {
698 target: stage
699 ignoreUnknownSignals: true
700 onItemSnapshotRequested: itemGrabber.capture(item)
701 }
702 }
704 Cursor {
705 id: cursor
706 visible: shell.hasMouse
707 z: itemGrabber.z + 1
708 topBoundaryOffset: panel.panelHeight
710 confiningItem: stage.itemConfiningMouseCursor
712 property bool mouseNeverMoved: true
713 Binding {
714 target: cursor; property: "x"; value: shell.width / 2
715 when: cursor.mouseNeverMoved && cursor.visible
716 }
717 Binding {
718 target: cursor; property: "y"; value: shell.height / 2
719 when: cursor.mouseNeverMoved && cursor.visible
720 }
722 height: units.gu(3)
724 readonly property var previewRectangle: stage.previewRectangle.target &&
725 stage.previewRectangle.target.dragging ?
726 stage.previewRectangle : null
728 onPushedLeftBoundary: {
729 if (buttons === Qt.NoButton) {
730 launcher.pushEdge(amount);
731 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
732 previewRectangle.maximizeLeft(amount);
733 }
734 }
736 onPushedRightBoundary: {
737 if (buttons === Qt.NoButton) {
738 stage.pushRightEdge(amount);
739 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
740 previewRectangle.maximizeRight(amount);
741 }
742 }
744 onPushedTopBoundary: {
745 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
746 previewRectangle.maximize(amount);
747 }
748 }
749 onPushedTopLeftCorner: {
750 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
751 previewRectangle.maximizeTopLeft(amount);
752 }
753 }
754 onPushedTopRightCorner: {
755 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
756 previewRectangle.maximizeTopRight(amount);
757 }
758 }
759 onPushedBottomLeftCorner: {
760 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
761 previewRectangle.maximizeBottomLeft(amount);
762 }
763 }
764 onPushedBottomRightCorner: {
765 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
766 previewRectangle.maximizeBottomRight(amount);
767 }
768 }
769 onPushStopped: {
770 if (previewRectangle) {
771 previewRectangle.stop();
772 }
773 }
775 onMouseMoved: {
776 mouseNeverMoved = false;
777 cursor.opacity = 1;
778 }
779 }
781 // non-visual object
782 KeymapSwitcher {}
784 Rectangle {
785 id: shutdownFadeOutRectangle
786 z: cursor.z + 1
787 enabled: false
788 visible: false
789 color: "black"
790 anchors.fill: parent
791 opacity: 0.0
792 NumberAnimation on opacity {
793 id: shutdownFadeOut
794 from: 0.0
795 to: 1.0
796 onStopped: {
797 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
798 DBusUnitySessionService.shutdown();
799 }
800 }
801 }
802 }