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