on a3657be help sign in
unity8/qml/Shell.qml
812 lines 29 kB history raw
1/*
2 * Copyright (C) 2018 The UBports project
3 * Copyright (C) 2013-2016 Canonical, Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18import QtQuick 2.4
19import QtQuick.Window 2.2
20import AccountsService 0.1
21import Unity.Application 0.1
22import Ubuntu.Components 1.3
23import Ubuntu.Components.Popups 1.3
24import Ubuntu.Gestures 0.1
25import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Connectivity 0.1
27import Unity.Launcher 0.1
28import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
29import GSettings 1.0
30import ImageCache 0.1
31import Utils 0.1
32import Powerd 0.1
33import SessionBroadcast 0.1
34import "Greeter"
35import "Launcher"
36import "Panel"
37import "Components"
38import "Notifications"
39import "Stage"
40import "Tutorial"
41import "Wizard"
42import Unity.Notifications 1.0 as NotificationBackend
43import Unity.Session 0.1
44import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
46import Cursor 1.1
47import WindowManager 0.1
50StyledItem {
51 id: shell
53 theme.name: "Ubuntu.Components.Themes.SuruDark"
55 // to be set from outside
56 property int orientationAngle: 0
57 property int orientation
58 property Orientations orientations
59 property real nativeWidth
60 property real nativeHeight
61 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
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://" + Constants.defaultWallpaper
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
287 inputMethodRect: inputMethod.visibleRect
289 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
290 ? "phone"
291 : shell.usageScenario
293 mode: usageScenario == "phone" ? "staged"
294 : usageScenario == "tablet" ? "stagedWithSideStage"
295 : "windowed"
297 shellOrientation: shell.orientation
298 shellOrientationAngle: shell.orientationAngle
299 orientations: shell.orientations
300 nativeWidth: shell.nativeWidth
301 nativeHeight: shell.nativeHeight
303 interactive: (!greeter || !greeter.shown)
304 && panel.indicators.fullyClosed
305 && launcher.progress == 0
306 && !notifications.useModal
308 onInteractiveChanged: { if (interactive) { focus = true; } }
310 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
311 suspended: greeter.shown
312 keepDashRunning: launcher.shown || launcher.dashSwipe
313 altTabPressed: physicalKeysMapper.altTabPressed
314 oskEnabled: shell.oskEnabled
315 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
316 }
317 }
319 InputMethod {
320 id: inputMethod
321 objectName: "inputMethod"
322 anchors {
323 fill: parent
324 topMargin: panel.panelHeight
325 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
326 }
327 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
328 }
330 Loader {
331 id: greeterLoader
332 objectName: "greeterLoader"
333 anchors.fill: parent
334 anchors.topMargin: panel.panelHeight
335 sourceComponent: shell.mode != "shell" ? integratedGreeter :
336 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
337 onLoaded: {
338 item.objectName = "greeter"
339 }
340 }
342 Component {
343 id: integratedGreeter
344 Greeter {
346 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
347 hides: [launcher, panel.indicators]
348 tabletMode: shell.usageScenario != "phone"
349 launcherOffset: launcher.progress
350 forcedUnlock: wizard.active && !wizard.isUpdate || shell.mode === "full-shell"
351 background: wallpaperResolver.cachedBackground
352 hasCustomBackground: wallpaperResolver.hasCustomBackground
353 allowFingerprint: !dialogs.hasActiveDialog &&
354 !notifications.topmostIsFullscreen &&
355 !panel.indicators.shown
357 // avoid overlapping with Launcher's edge drag area
358 // FIXME: Fix TouchRegistry & friends and remove this workaround
359 // Issue involves launcher's DDA getting disabled on a long
360 // left-edge drag
361 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
363 onSessionStarted: {
364 launcher.hide();
365 }
367 onTease: {
368 if (!tutorial.running) {
369 launcher.tease();
370 }
371 }
373 onEmergencyCall: startLockedApp("dialer-app")
374 }
375 }
377 Timer {
378 // See powerConnection for why this is useful
379 id: showGreeterDelayed
380 interval: 1
381 onTriggered: {
382 greeter.forceShow();
383 }
384 }
386 Connections {
387 id: callConnection
388 target: callManager
390 onHasCallsChanged: {
391 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
392 // We just received an incoming call while locked. The
393 // indicator will have already launched dialer-app for us, but
394 // there is a race between "hasCalls" changing and the dialer
395 // starting up. So in case we lose that race, we'll start/
396 // focus the dialer ourselves here too. Even if the indicator
397 // didn't launch the dialer for some reason (or maybe a call
398 // started via some other means), if an active call is
399 // happening, we want to be in the dialer.
400 startLockedApp("dialer-app")
401 }
402 }
403 }
405 Connections {
406 id: powerConnection
407 target: Powerd
409 onStatusChanged: {
410 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
411 !callManager.hasCalls && !wizard.active) {
412 // We don't want to simply call greeter.showNow() here, because
413 // that will take too long. Qt will delay button event
414 // handling until the greeter is done loading and may think the
415 // user held down the power button the whole time, leading to a
416 // power dialog being shown. Instead, delay showing the
417 // greeter until we've finished handling the event. We could
418 // make the greeter load asynchronously instead, but that
419 // introduces a whole host of timing issues, especially with
420 // its animations. So this is simpler.
421 showGreeterDelayed.start();
422 }
423 }
424 }
426 function showHome() {
427 greeter.notifyUserRequestedApp();
429 if (shell.mode === "greeter") {
430 SessionBroadcast.requestHomeShown(AccountsService.user);
431 } else {
432 var animate = !LightDMService.greeter.active && !stages.shown;
433 dash.setCurrentScope(0, animate, false);
434 ApplicationManager.requestFocusApplication("unity8-dash");
435 }
436 }
438 function showDash() {
439 if (greeter.notifyShowingDashFromDrag()) {
440 launcher.fadeOut();
441 }
443 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
444 && (ApplicationManager.focusedApplicationId != "unity8-dash" || stage.spreadShown)) {
445 ApplicationManager.requestFocusApplication("unity8-dash")
446 launcher.fadeOut();
447 stage.closeSpread();
448 }
449 }
451 Item {
452 id: overlay
453 z: 10
455 anchors.fill: parent
457 Panel {
458 id: panel
459 objectName: "panel"
460 anchors.fill: parent //because this draws indicator menus
461 indicators {
462 hides: [launcher]
463 available: tutorial.panelEnabled
464 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
465 && (!greeter || !greeter.hasLockedApp)
466 && !shell.waitingOnGreeter
467 && settings.enableIndicatorMenu
468 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
470 minimizedPanelHeight: units.gu(3)
471 expandedPanelHeight: units.gu(7)
473 indicatorsModel: Indicators.IndicatorsModel {
474 // tablet and phone both use the same profile
475 // FIXME: use just "phone" for greeter too, but first fix
476 // greeter app launching to either load the app inside the
477 // greeter or tell the session to load the app. This will
478 // involve taking the url-dispatcher dbus name and using
479 // SessionBroadcast to tell the session.
480 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
481 Component.onCompleted: load();
482 }
483 }
485 callHint {
486 greeterShown: greeter.shown
487 }
489 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
490 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
491 : false
492 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
493 || greeter.hasLockedApp
494 locked: greeter && greeter.active
495 }
497 Launcher {
498 id: launcher
499 objectName: "launcher"
501 /*
502 * Since the Dash doesn't have the same controll over surfaces that the
503 * Shell does, it can't slowly move the scope out of the way, as the shell
504 * does with apps, and the dash is show instantly. This allows for some
505 * leeway and prevents accidental home swipes.
506 */
507 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
508 readonly property bool dashSwipe: progress > offset
510 anchors.top: parent.top
511 anchors.topMargin: inverted ? 0 : panel.panelHeight
512 anchors.bottom: parent.bottom
513 width: parent.width
514 dragAreaWidth: shell.edgeSize
515 available: tutorial.launcherEnabled
516 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
517 && !greeter.hasLockedApp
518 && !shell.waitingOnGreeter
519 && settings.enableLauncher
520 inverted: shell.usageScenario !== "desktop"
521 superPressed: physicalKeysMapper.superPressed
522 superTabPressed: physicalKeysMapper.superTabPressed
523 panelWidth: units.gu(settings.launcherWidth)
524 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
526 onShowDashHome: showHome()
527 onDash: showDash()
528 onDashSwipeChanged: {
529 if (dashSwipe) {
530 dash.setCurrentScope(0, false, true)
531 }
532 }
533 onLauncherApplicationSelected: {
534 greeter.notifyUserRequestedApp();
535 shell.activateApplication(appId);
536 }
537 onShownChanged: {
538 if (shown) {
539 panel.indicators.hide()
540 }
541 }
542 onFocusChanged: {
543 if (!focus) {
544 stage.focus = true;
545 }
546 }
548 GlobalShortcut {
549 shortcut: Qt.AltModifier | Qt.Key_F1
550 onTriggered: {
551 launcher.openForKeyboardNavigation();
552 }
553 }
554 GlobalShortcut {
555 shortcut: Qt.MetaModifier | Qt.Key_0
556 onTriggered: {
557 if (LauncherModel.get(9)) {
558 activateApplication(LauncherModel.get(9).appId);
559 }
560 }
561 }
562 Repeater {
563 model: 9
564 GlobalShortcut {
565 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
566 onTriggered: {
567 if (LauncherModel.get(index)) {
568 activateApplication(LauncherModel.get(index).appId);
569 }
570 }
571 }
572 }
573 }
575 KeyboardShortcutsOverlay {
576 objectName: "shortcutsOverlay"
577 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
578 && height < parent.height - padding - panel.panelHeight
579 anchors.centerIn: parent
580 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
581 anchors.verticalCenterOffset: panel.panelHeight/2
582 visible: opacity > 0
583 opacity: enabled ? 0.95 : 0
585 Behavior on opacity {
586 UbuntuNumberAnimation {}
587 }
588 }
590 Tutorial {
591 id: tutorial
592 objectName: "tutorial"
593 anchors.fill: parent
595 paused: callManager.hasCalls || !greeter || greeter.active ||
596 wizard.active
597 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
598 inputMethod.visible ||
599 (launcher.shown && !launcher.lockedVisible) ||
600 panel.indicators.shown || stage.rightEdgeDragProgress > 0
601 usageScenario: shell.usageScenario
602 lastInputTimestamp: inputFilter.lastInputTimestamp
603 launcher: launcher
604 panel: panel
605 stage: stage
606 }
608 Wizard {
609 id: wizard
610 objectName: "wizard"
611 anchors.fill: parent
612 deferred: shell.mode === "greeter"
614 function unlockWhenDoneWithWizard() {
615 if (!active) {
616 Connectivity.unlockAllModems();
617 }
618 }
620 Component.onCompleted: unlockWhenDoneWithWizard()
621 onActiveChanged: unlockWhenDoneWithWizard()
622 }
624 MouseArea { // modal notifications prevent interacting with other contents
625 anchors.fill: parent
626 visible: notifications.useModal
627 enabled: visible
628 }
630 Notifications {
631 id: notifications
633 model: NotificationBackend.Model
634 margin: units.gu(1)
635 hasMouse: shell.hasMouse
636 background: wallpaperResolver.cachedBackground
638 y: topmostIsFullscreen ? 0 : panel.panelHeight
639 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
641 states: [
642 State {
643 name: "narrow"
644 when: overlay.width <= units.gu(60)
645 AnchorChanges {
646 target: notifications
647 anchors.left: parent.left
648 anchors.right: parent.right
649 }
650 },
651 State {
652 name: "wide"
653 when: overlay.width > units.gu(60)
654 AnchorChanges {
655 target: notifications
656 anchors.left: undefined
657 anchors.right: parent.right
658 }
659 PropertyChanges { target: notifications; width: units.gu(38) }
660 }
661 ]
662 }
663 }
665 Dialogs {
666 id: dialogs
667 objectName: "dialogs"
668 anchors.fill: parent
669 z: overlay.z + 10
670 usageScenario: shell.usageScenario
671 onPowerOffClicked: {
672 shutdownFadeOutRectangle.enabled = true;
673 shutdownFadeOutRectangle.visible = true;
674 shutdownFadeOut.start();
675 }
676 }
678 Connections {
679 target: SessionBroadcast
680 onShowHome: if (shell.mode !== "greeter") showHome()
681 }
683 URLDispatcher {
684 id: urlDispatcher
685 objectName: "urlDispatcher"
686 active: shell.mode === "greeter"
687 onUrlRequested: shell.activateURL(url)
688 }
690 ItemGrabber {
691 id: itemGrabber
692 anchors.fill: parent
693 z: dialogs.z + 10
694 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
695 Connections {
696 target: stage
697 ignoreUnknownSignals: true
698 onItemSnapshotRequested: itemGrabber.capture(item)
699 }
700 }
702 Timer {
703 id: cursorHidingTimer
704 interval: 3000
705 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
706 onTriggered: cursor.opacity = 0;
707 }
709 Cursor {
710 id: cursor
711 objectName: "cursor"
712 visible: shell.hasMouse
713 z: itemGrabber.z + 1
714 topBoundaryOffset: panel.panelHeight
716 confiningItem: stage.itemConfiningMouseCursor
718 property bool mouseNeverMoved: true
719 Binding {
720 target: cursor; property: "x"; value: shell.width / 2
721 when: cursor.mouseNeverMoved && cursor.visible
722 }
723 Binding {
724 target: cursor; property: "y"; value: shell.height / 2
725 when: cursor.mouseNeverMoved && cursor.visible
726 }
728 height: units.gu(3)
730 readonly property var previewRectangle: stage.previewRectangle.target &&
731 stage.previewRectangle.target.dragging ?
732 stage.previewRectangle : null
734 onPushedLeftBoundary: {
735 if (buttons === Qt.NoButton) {
736 launcher.pushEdge(amount);
737 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
738 previewRectangle.maximizeLeft(amount);
739 }
740 }
742 onPushedRightBoundary: {
743 if (buttons === Qt.NoButton) {
744 stage.pushRightEdge(amount);
745 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
746 previewRectangle.maximizeRight(amount);
747 }
748 }
750 onPushedTopBoundary: {
751 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
752 previewRectangle.maximize(amount);
753 }
754 }
755 onPushedTopLeftCorner: {
756 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
757 previewRectangle.maximizeTopLeft(amount);
758 }
759 }
760 onPushedTopRightCorner: {
761 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
762 previewRectangle.maximizeTopRight(amount);
763 }
764 }
765 onPushedBottomLeftCorner: {
766 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
767 previewRectangle.maximizeBottomLeft(amount);
768 }
769 }
770 onPushedBottomRightCorner: {
771 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
772 previewRectangle.maximizeBottomRight(amount);
773 }
774 }
775 onPushStopped: {
776 if (previewRectangle) {
777 previewRectangle.stop();
778 }
779 }
781 onMouseMoved: {
782 mouseNeverMoved = false;
783 cursor.opacity = 1;
784 }
786 Behavior on opacity { UbuntuNumberAnimation {} }
787 }
789 // non-visual objects
790 KeymapSwitcher {}
791 BrightnessControl {}
793 Rectangle {
794 id: shutdownFadeOutRectangle
795 z: cursor.z + 1
796 enabled: false
797 visible: false
798 color: "black"
799 anchors.fill: parent
800 opacity: 0.0
801 NumberAnimation on opacity {
802 id: shutdownFadeOut
803 from: 0.0
804 to: 1.0
805 onStopped: {
806 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
807 DBusUnitySessionService.shutdown();
808 }
809 }
810 }
811 }