on c1fc94e help sign in
unity8/qml/Shell.qml
805 lines 28 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 1.0
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 panelAreaShowProgress: panel.panelAreaShowProgress
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(launcher.ignoreHideIfMouseOverLauncher);
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://" + Constants.defaultWallpaper
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 SurfaceManager {
268 id: surfaceMan
269 objectName: "surfaceManager"
270 }
271 TopLevelWindowModel {
272 id: topLevelSurfaceList
273 objectName: "topLevelSurfaceList"
274 applicationManager: ApplicationManager // it's a singleton
275 surfaceManager: surfaceMan
276 }
278 Stage {
279 id: stage
280 objectName: "stage"
281 anchors.fill: parent
282 focus: true
284 dragAreaWidth: shell.edgeSize
285 background: wallpaperResolver.background
287 applicationManager: ApplicationManager
288 topLevelSurfaceList: topLevelSurfaceList
289 inputMethodRect: inputMethod.visibleRect
291 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
292 ? "phone"
293 : shell.usageScenario
295 mode: usageScenario == "phone" ? "staged"
296 : usageScenario == "tablet" ? "stagedWithSideStage"
297 : "windowed"
299 shellOrientation: shell.orientation
300 shellOrientationAngle: shell.orientationAngle
301 orientations: shell.orientations
302 nativeWidth: shell.nativeWidth
303 nativeHeight: shell.nativeHeight
305 interactive: (!greeter || !greeter.shown)
306 && panel.indicators.fullyClosed
307 && !notifications.useModal
309 onInteractiveChanged: { if (interactive) { focus = true; } }
311 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
312 suspended: greeter.shown
313 altTabPressed: physicalKeysMapper.altTabPressed
314 oskEnabled: shell.oskEnabled
315 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
317 onSpreadShownChanged: {
318 panel.indicators.hide();
319 panel.applicationMenus.hide();
320 }
321 }
322 }
324 InputMethod {
325 id: inputMethod
326 objectName: "inputMethod"
327 surface: topLevelSurfaceList.inputMethodSurface
328 anchors {
329 fill: parent
330 topMargin: panel.panelHeight
331 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
332 }
333 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
334 }
336 Loader {
337 id: greeterLoader
338 objectName: "greeterLoader"
339 anchors.fill: parent
340 anchors.topMargin: panel.panelHeight
341 sourceComponent: shell.mode != "shell" ? integratedGreeter :
342 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
343 onLoaded: {
344 item.objectName = "greeter"
345 }
346 }
348 Component {
349 id: integratedGreeter
350 Greeter {
352 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
353 hides: [launcher, panel.indicators, panel.applicationMenus]
354 tabletMode: shell.usageScenario != "phone"
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 Item {
444 id: overlay
445 z: 10
447 anchors.fill: parent
449 Panel {
450 id: panel
451 objectName: "panel"
452 anchors.fill: parent //because this draws indicator menus
454 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
455 minimizedPanelHeight: units.gu(3)
456 expandedPanelHeight: units.gu(7)
457 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
458 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
460 indicators {
461 hides: [launcher]
462 available: tutorial.panelEnabled
463 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
464 && (!greeter || !greeter.hasLockedApp)
465 && !shell.waitingOnGreeter
466 && settings.enableIndicatorMenu
468 model: Indicators.IndicatorsModel {
469 // tablet and phone both use the same profile
470 // FIXME: use just "phone" for greeter too, but first fix
471 // greeter app launching to either load the app inside the
472 // greeter or tell the session to load the app. This will
473 // involve taking the url-dispatcher dbus name and using
474 // SessionBroadcast to tell the session.
475 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
476 Component.onCompleted: load();
477 }
478 }
480 applicationMenus {
481 hides: [launcher]
482 available: (!greeter || !greeter.shown)
483 && !shell.waitingOnGreeter
484 }
486 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
487 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
488 : false
489 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
490 || greeter.hasLockedApp
491 greeterShown: greeter && greeter.shown
492 }
494 Launcher {
495 id: launcher
496 objectName: "launcher"
498 anchors.top: parent.top
499 anchors.topMargin: inverted ? 0 : panel.panelHeight
500 anchors.bottom: parent.bottom
501 width: parent.width
502 dragAreaWidth: shell.edgeSize
503 available: tutorial.launcherEnabled
504 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
505 && !greeter.hasLockedApp
506 && !shell.waitingOnGreeter
507 && settings.enableLauncher
508 inverted: shell.usageScenario !== "desktop"
509 superPressed: physicalKeysMapper.superPressed
510 superTabPressed: physicalKeysMapper.superTabPressed
511 panelWidth: units.gu(settings.launcherWidth)
512 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
513 blurSource: greeter.shown ? greeter : stages
514 topPanelHeight: panel.panelHeight
515 drawerEnabled: !greeter.shown
517 onShowDashHome: showHome()
518 onLauncherApplicationSelected: {
519 greeter.notifyUserRequestedApp();
520 shell.activateApplication(appId);
521 }
522 onShownChanged: {
523 if (shown) {
524 panel.indicators.hide()
525 }
526 }
527 onFocusChanged: {
528 if (!focus) {
529 stage.focus = true;
530 }
531 }
533 GlobalShortcut {
534 shortcut: Qt.MetaModifier | Qt.Key_A
535 onTriggered: {
536 launcher.openDrawer(true);
537 }
538 }
539 GlobalShortcut {
540 shortcut: Qt.AltModifier | Qt.Key_F1
541 onTriggered: {
542 launcher.openForKeyboardNavigation();
543 }
544 }
545 GlobalShortcut {
546 shortcut: Qt.MetaModifier | Qt.Key_0
547 onTriggered: {
548 if (LauncherModel.get(9)) {
549 activateApplication(LauncherModel.get(9).appId);
550 }
551 }
552 }
553 Repeater {
554 model: 9
555 GlobalShortcut {
556 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
557 onTriggered: {
558 if (LauncherModel.get(index)) {
559 activateApplication(LauncherModel.get(index).appId);
560 }
561 }
562 }
563 }
564 }
566 KeyboardShortcutsOverlay {
567 objectName: "shortcutsOverlay"
568 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
569 && height < parent.height - padding - panel.panelHeight
570 anchors.centerIn: parent
571 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
572 anchors.verticalCenterOffset: panel.panelHeight/2
573 visible: opacity > 0
574 opacity: enabled ? 0.95 : 0
576 Behavior on opacity {
577 UbuntuNumberAnimation {}
578 }
579 }
581 Tutorial {
582 id: tutorial
583 objectName: "tutorial"
584 anchors.fill: parent
586 paused: callManager.hasCalls || !greeter || greeter.active ||
587 wizard.active
588 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
589 inputMethod.visible ||
590 (launcher.shown && !launcher.lockedVisible) ||
591 panel.indicators.shown || stage.rightEdgeDragProgress > 0
592 usageScenario: shell.usageScenario
593 lastInputTimestamp: inputFilter.lastInputTimestamp
594 launcher: launcher
595 panel: panel
596 stage: stage
597 }
599 Wizard {
600 id: wizard
601 objectName: "wizard"
602 anchors.fill: parent
603 deferred: shell.mode === "greeter"
605 function unlockWhenDoneWithWizard() {
606 if (!active) {
607 Connectivity.unlockAllModems();
608 }
609 }
611 Component.onCompleted: unlockWhenDoneWithWizard()
612 onActiveChanged: unlockWhenDoneWithWizard()
613 }
615 MouseArea { // modal notifications prevent interacting with other contents
616 anchors.fill: parent
617 visible: notifications.useModal
618 enabled: visible
619 }
621 Notifications {
622 id: notifications
624 model: NotificationBackend.Model
625 margin: units.gu(1)
626 hasMouse: shell.hasMouse
627 background: wallpaperResolver.cachedBackground
629 y: topmostIsFullscreen ? 0 : panel.panelHeight
630 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
632 states: [
633 State {
634 name: "narrow"
635 when: overlay.width <= units.gu(60)
636 AnchorChanges {
637 target: notifications
638 anchors.left: parent.left
639 anchors.right: parent.right
640 }
641 },
642 State {
643 name: "wide"
644 when: overlay.width > units.gu(60)
645 AnchorChanges {
646 target: notifications
647 anchors.left: undefined
648 anchors.right: parent.right
649 }
650 PropertyChanges { target: notifications; width: units.gu(38) }
651 }
652 ]
653 }
654 }
656 Dialogs {
657 id: dialogs
658 objectName: "dialogs"
659 anchors.fill: parent
660 z: overlay.z + 10
661 usageScenario: shell.usageScenario
662 onPowerOffClicked: {
663 shutdownFadeOutRectangle.enabled = true;
664 shutdownFadeOutRectangle.visible = true;
665 shutdownFadeOut.start();
666 }
667 }
669 Connections {
670 target: SessionBroadcast
671 onShowHome: if (shell.mode !== "greeter") showHome()
672 }
674 URLDispatcher {
675 id: urlDispatcher
676 objectName: "urlDispatcher"
677 active: shell.mode === "greeter"
678 onUrlRequested: shell.activateURL(url)
679 }
681 ItemGrabber {
682 id: itemGrabber
683 anchors.fill: parent
684 z: dialogs.z + 10
685 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
686 Connections {
687 target: stage
688 ignoreUnknownSignals: true
689 onItemSnapshotRequested: itemGrabber.capture(item)
690 }
691 }
693 Timer {
694 id: cursorHidingTimer
695 interval: 3000
696 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
697 onTriggered: cursor.opacity = 0;
698 }
700 Cursor {
701 id: cursor
702 objectName: "cursor"
703 visible: shell.hasMouse
704 z: itemGrabber.z + 1
705 topBoundaryOffset: panel.panelHeight
707 confiningItem: stage.itemConfiningMouseCursor
709 property bool mouseNeverMoved: true
710 Binding {
711 target: cursor; property: "x"; value: shell.width / 2
712 when: cursor.mouseNeverMoved && cursor.visible
713 }
714 Binding {
715 target: cursor; property: "y"; value: shell.height / 2
716 when: cursor.mouseNeverMoved && cursor.visible
717 }
719 height: units.gu(3)
721 readonly property var previewRectangle: stage.previewRectangle.target &&
722 stage.previewRectangle.target.dragging ?
723 stage.previewRectangle : null
725 onPushedLeftBoundary: {
726 if (buttons === Qt.NoButton) {
727 launcher.pushEdge(amount);
728 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
729 previewRectangle.maximizeLeft(amount);
730 }
731 }
733 onPushedRightBoundary: {
734 if (buttons === Qt.NoButton) {
735 stage.pushRightEdge(amount);
736 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
737 previewRectangle.maximizeRight(amount);
738 }
739 }
741 onPushedTopBoundary: {
742 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
743 previewRectangle.maximize(amount);
744 }
745 }
746 onPushedTopLeftCorner: {
747 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
748 previewRectangle.maximizeTopLeft(amount);
749 }
750 }
751 onPushedTopRightCorner: {
752 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
753 previewRectangle.maximizeTopRight(amount);
754 }
755 }
756 onPushedBottomLeftCorner: {
757 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
758 previewRectangle.maximizeBottomLeft(amount);
759 }
760 }
761 onPushedBottomRightCorner: {
762 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
763 previewRectangle.maximizeBottomRight(amount);
764 }
765 }
766 onPushStopped: {
767 if (previewRectangle) {
768 previewRectangle.stop();
769 }
770 }
772 onMouseMoved: {
773 mouseNeverMoved = false;
774 cursor.opacity = 1;
775 }
777 Behavior on opacity { UbuntuNumberAnimation {} }
778 }
780 // non-visual objects
781 KeymapSwitcher {
782 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
783 }
784 BrightnessControl {}
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 }