on 400e2db help sign in
unity8/qml/Shell.qml
849 lines 30 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
71 property bool hasKeyboard: 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(launcher.ignoreHideIfMouseOverLauncher);
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 ApplicationManager.requestFocusApplication("unity8-dash");
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 SurfaceManager {
269 id: surfaceMan
270 objectName: "surfaceManager"
271 }
272 TopLevelWindowModel {
273 id: topLevelSurfaceList
274 objectName: "topLevelSurfaceList"
275 applicationManager: ApplicationManager // it's a singleton
276 surfaceManager: surfaceMan
277 }
279 Stage {
280 id: stage
281 objectName: "stage"
282 anchors.fill: parent
283 focus: true
285 dragAreaWidth: shell.edgeSize
286 background: wallpaperResolver.background
288 applicationManager: ApplicationManager
289 topLevelSurfaceList: topLevelSurfaceList
290 inputMethodRect: inputMethod.visibleRect
292 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
293 ? "phone"
294 : shell.usageScenario
296 mode: usageScenario == "phone" ? "staged"
297 : usageScenario == "tablet" ? "stagedWithSideStage"
298 : "windowed"
300 shellOrientation: shell.orientation
301 shellOrientationAngle: shell.orientationAngle
302 orientations: shell.orientations
303 nativeWidth: shell.nativeWidth
304 nativeHeight: shell.nativeHeight
306 interactive: (!greeter || !greeter.shown)
307 && panel.indicators.fullyClosed
308 && !notifications.useModal
310 onInteractiveChanged: { if (interactive) { focus = true; } }
312 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
313 suspended: greeter.shown
314 altTabPressed: physicalKeysMapper.altTabPressed
315 oskEnabled: shell.oskEnabled
316 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
318 onSpreadShownChanged: {
319 panel.indicators.hide();
320 panel.applicationMenus.hide();
321 }
322 }
324 TouchGestureArea {
325 anchors.fill: stage
327 minimumTouchPoints: 4
328 maximumTouchPoints: minimumTouchPoints
330 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
331 touchPoints.length >= minimumTouchPoints &&
332 touchPoints.length <= maximumTouchPoints
333 property bool wasPressed: false
335 onRecognisedPressChanged: {
336 if (recognisedPress) {
337 wasPressed = true;
338 }
339 }
341 onStatusChanged: {
342 if (status !== TouchGestureArea.Recognized) {
343 if (status === TouchGestureArea.WaitingForTouch) {
344 if (wasPressed && !dragging) {
345 launcher.openDrawer(true);
346 }
347 }
348 wasPressed = false;
349 }
350 }
351 }
352 }
354 InputMethod {
355 id: inputMethod
356 objectName: "inputMethod"
357 surface: topLevelSurfaceList.inputMethodSurface
358 anchors {
359 fill: parent
360 topMargin: panel.panelHeight
361 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
362 }
363 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
364 }
366 Loader {
367 id: greeterLoader
368 objectName: "greeterLoader"
369 anchors.fill: parent
370 anchors.topMargin: panel.panelHeight
371 sourceComponent: shell.mode != "shell" ? integratedGreeter :
372 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
373 onLoaded: {
374 item.objectName = "greeter"
375 }
376 property bool openDrawerAfterUnlock: false
377 Connections {
378 target: greeter
379 onActiveChanged: {
380 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
381 launcher.openDrawer(false);
382 greeterLoader.openDrawerAfterUnlock = false;
383 }
384 }
385 }
386 }
388 Component {
389 id: integratedGreeter
390 Greeter {
392 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
393 hides: [launcher, panel.indicators, panel.applicationMenus]
394 tabletMode: shell.usageScenario != "phone"
395 forcedUnlock: wizard.active || shell.mode === "full-shell"
396 background: wallpaperResolver.cachedBackground
397 hasCustomBackground: wallpaperResolver.hasCustomBackground
398 allowFingerprint: !dialogs.hasActiveDialog &&
399 !notifications.topmostIsFullscreen &&
400 !panel.indicators.shown
402 // avoid overlapping with Launcher's edge drag area
403 // FIXME: Fix TouchRegistry & friends and remove this workaround
404 // Issue involves launcher's DDA getting disabled on a long
405 // left-edge drag
406 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
408 onSessionStarted: {
409 launcher.hide();
410 }
412 onTease: {
413 if (!tutorial.running) {
414 launcher.tease();
415 }
416 }
418 onEmergencyCall: startLockedApp("dialer-app")
419 }
420 }
422 Timer {
423 // See powerConnection for why this is useful
424 id: showGreeterDelayed
425 interval: 1
426 onTriggered: {
427 greeter.forceShow();
428 }
429 }
431 Connections {
432 id: callConnection
433 target: callManager
435 onHasCallsChanged: {
436 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
437 // We just received an incoming call while locked. The
438 // indicator will have already launched dialer-app for us, but
439 // there is a race between "hasCalls" changing and the dialer
440 // starting up. So in case we lose that race, we'll start/
441 // focus the dialer ourselves here too. Even if the indicator
442 // didn't launch the dialer for some reason (or maybe a call
443 // started via some other means), if an active call is
444 // happening, we want to be in the dialer.
445 startLockedApp("dialer-app")
446 }
447 }
448 }
450 Connections {
451 id: powerConnection
452 target: Powerd
454 onStatusChanged: {
455 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
456 !callManager.hasCalls && !wizard.active) {
457 // We don't want to simply call greeter.showNow() here, because
458 // that will take too long. Qt will delay button event
459 // handling until the greeter is done loading and may think the
460 // user held down the power button the whole time, leading to a
461 // power dialog being shown. Instead, delay showing the
462 // greeter until we've finished handling the event. We could
463 // make the greeter load asynchronously instead, but that
464 // introduces a whole host of timing issues, especially with
465 // its animations. So this is simpler.
466 showGreeterDelayed.start();
467 }
468 }
469 }
471 function showHome() {
472 greeter.notifyUserRequestedApp();
474 if (shell.mode === "greeter") {
475 SessionBroadcast.requestHomeShown(AccountsService.user);
476 } else {
477 if (!greeter.active) {
478 launcher.openDrawer(false);
479 } else {
480 greeterLoader.openDrawerAfterUnlock = true;
481 }
482 }
483 }
485 Item {
486 id: overlay
487 z: 10
489 anchors.fill: parent
491 Panel {
492 id: panel
493 objectName: "panel"
494 anchors.fill: parent //because this draws indicator menus
496 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
497 minimizedPanelHeight: units.gu(3)
498 expandedPanelHeight: units.gu(7)
499 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
500 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
502 indicators {
503 hides: [launcher]
504 available: tutorial.panelEnabled
505 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
506 && (!greeter || !greeter.hasLockedApp)
507 && !shell.waitingOnGreeter
508 && settings.enableIndicatorMenu
510 model: Indicators.IndicatorsModel {
511 // tablet and phone both use the same profile
512 // FIXME: use just "phone" for greeter too, but first fix
513 // greeter app launching to either load the app inside the
514 // greeter or tell the session to load the app. This will
515 // involve taking the url-dispatcher dbus name and using
516 // SessionBroadcast to tell the session.
517 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
518 Component.onCompleted: load();
519 }
520 }
522 applicationMenus {
523 hides: [launcher]
524 available: (!greeter || !greeter.shown)
525 && !shell.waitingOnGreeter
526 }
528 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
529 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
530 : false
531 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
532 || greeter.hasLockedApp
533 greeterShown: greeter && greeter.shown
534 }
536 Launcher {
537 id: launcher
538 objectName: "launcher"
540 anchors.top: parent.top
541 anchors.topMargin: inverted ? 0 : panel.panelHeight
542 anchors.bottom: parent.bottom
543 width: parent.width
544 dragAreaWidth: shell.edgeSize
545 available: tutorial.launcherEnabled
546 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
547 && !greeter.hasLockedApp
548 && !shell.waitingOnGreeter
549 && settings.enableLauncher
550 inverted: shell.usageScenario !== "desktop"
551 superPressed: physicalKeysMapper.superPressed
552 superTabPressed: physicalKeysMapper.superTabPressed
553 panelWidth: units.gu(settings.launcherWidth)
554 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
555 blurSource: greeter.shown ? greeter : stages
556 topPanelHeight: panel.panelHeight
557 drawerEnabled: !greeter.active
559 onShowDashHome: showHome()
560 onLauncherApplicationSelected: {
561 greeter.notifyUserRequestedApp();
562 shell.activateApplication(appId);
563 }
564 onShownChanged: {
565 if (shown) {
566 panel.indicators.hide()
567 }
568 }
569 onFocusChanged: {
570 if (!focus) {
571 stage.focus = true;
572 }
573 }
575 GlobalShortcut {
576 shortcut: Qt.MetaModifier | Qt.Key_A
577 onTriggered: {
578 launcher.openDrawer(true);
579 }
580 }
581 GlobalShortcut {
582 shortcut: Qt.AltModifier | Qt.Key_F1
583 onTriggered: {
584 launcher.openForKeyboardNavigation();
585 }
586 }
587 GlobalShortcut {
588 shortcut: Qt.MetaModifier | Qt.Key_0
589 onTriggered: {
590 if (LauncherModel.get(9)) {
591 activateApplication(LauncherModel.get(9).appId);
592 }
593 }
594 }
595 Repeater {
596 model: 9
597 GlobalShortcut {
598 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
599 onTriggered: {
600 if (LauncherModel.get(index)) {
601 activateApplication(LauncherModel.get(index).appId);
602 }
603 }
604 }
605 }
606 }
608 KeyboardShortcutsOverlay {
609 objectName: "shortcutsOverlay"
610 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
611 && height < parent.height - padding - panel.panelHeight
612 anchors.centerIn: parent
613 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
614 anchors.verticalCenterOffset: panel.panelHeight/2
615 visible: opacity > 0
616 opacity: enabled ? 0.95 : 0
618 Behavior on opacity {
619 UbuntuNumberAnimation {}
620 }
621 }
623 Tutorial {
624 id: tutorial
625 objectName: "tutorial"
626 anchors.fill: parent
628 paused: callManager.hasCalls || !greeter || greeter.active ||
629 wizard.active
630 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
631 inputMethod.visible ||
632 (launcher.shown && !launcher.lockedVisible) ||
633 panel.indicators.shown || stage.rightEdgeDragProgress > 0
634 usageScenario: shell.usageScenario
635 lastInputTimestamp: inputFilter.lastInputTimestamp
636 launcher: launcher
637 panel: panel
638 stage: stage
639 }
641 Wizard {
642 id: wizard
643 objectName: "wizard"
644 anchors.fill: parent
645 deferred: shell.mode === "greeter"
647 function unlockWhenDoneWithWizard() {
648 if (!active) {
649 Connectivity.unlockAllModems();
650 }
651 }
653 Component.onCompleted: unlockWhenDoneWithWizard()
654 onActiveChanged: unlockWhenDoneWithWizard()
655 }
657 MouseArea { // modal notifications prevent interacting with other contents
658 anchors.fill: parent
659 visible: notifications.useModal
660 enabled: visible
661 }
663 Notifications {
664 id: notifications
666 model: NotificationBackend.Model
667 margin: units.gu(1)
668 hasMouse: shell.hasMouse
669 background: wallpaperResolver.cachedBackground
671 y: topmostIsFullscreen ? 0 : panel.panelHeight
672 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
674 states: [
675 State {
676 name: "narrow"
677 when: overlay.width <= units.gu(60)
678 AnchorChanges {
679 target: notifications
680 anchors.left: parent.left
681 anchors.right: parent.right
682 }
683 },
684 State {
685 name: "wide"
686 when: overlay.width > units.gu(60)
687 AnchorChanges {
688 target: notifications
689 anchors.left: undefined
690 anchors.right: parent.right
691 }
692 PropertyChanges { target: notifications; width: units.gu(38) }
693 }
694 ]
695 }
696 }
698 Dialogs {
699 id: dialogs
700 objectName: "dialogs"
701 anchors.fill: parent
702 visible: hasActiveDialog
703 z: overlay.z + 10
704 usageScenario: shell.usageScenario
705 hasKeyboard: shell.hasKeyboard
706 onPowerOffClicked: {
707 shutdownFadeOutRectangle.enabled = true;
708 shutdownFadeOutRectangle.visible = true;
709 shutdownFadeOut.start();
710 }
711 }
713 Connections {
714 target: SessionBroadcast
715 onShowHome: if (shell.mode !== "greeter") showHome()
716 }
718 URLDispatcher {
719 id: urlDispatcher
720 objectName: "urlDispatcher"
721 active: shell.mode === "greeter"
722 onUrlRequested: shell.activateURL(url)
723 }
725 ItemGrabber {
726 id: itemGrabber
727 anchors.fill: parent
728 z: dialogs.z + 10
729 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
730 Connections {
731 target: stage
732 ignoreUnknownSignals: true
733 onItemSnapshotRequested: itemGrabber.capture(item)
734 }
735 }
737 Timer {
738 id: cursorHidingTimer
739 interval: 3000
740 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
741 onTriggered: cursor.opacity = 0;
742 }
744 Cursor {
745 id: cursor
746 objectName: "cursor"
747 visible: shell.hasMouse
748 z: itemGrabber.z + 1
749 topBoundaryOffset: panel.panelHeight
751 confiningItem: stage.itemConfiningMouseCursor
753 property bool mouseNeverMoved: true
754 Binding {
755 target: cursor; property: "x"; value: shell.width / 2
756 when: cursor.mouseNeverMoved && cursor.visible
757 }
758 Binding {
759 target: cursor; property: "y"; value: shell.height / 2
760 when: cursor.mouseNeverMoved && cursor.visible
761 }
763 height: units.gu(3)
765 readonly property var previewRectangle: stage.previewRectangle.target &&
766 stage.previewRectangle.target.dragging ?
767 stage.previewRectangle : null
769 onPushedLeftBoundary: {
770 if (buttons === Qt.NoButton) {
771 launcher.pushEdge(amount);
772 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
773 previewRectangle.maximizeLeft(amount);
774 }
775 }
777 onPushedRightBoundary: {
778 if (buttons === Qt.NoButton) {
779 stage.pushRightEdge(amount);
780 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
781 previewRectangle.maximizeRight(amount);
782 }
783 }
785 onPushedTopBoundary: {
786 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
787 previewRectangle.maximize(amount);
788 }
789 }
790 onPushedTopLeftCorner: {
791 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
792 previewRectangle.maximizeTopLeft(amount);
793 }
794 }
795 onPushedTopRightCorner: {
796 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
797 previewRectangle.maximizeTopRight(amount);
798 }
799 }
800 onPushedBottomLeftCorner: {
801 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
802 previewRectangle.maximizeBottomLeft(amount);
803 }
804 }
805 onPushedBottomRightCorner: {
806 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
807 previewRectangle.maximizeBottomRight(amount);
808 }
809 }
810 onPushStopped: {
811 if (previewRectangle) {
812 previewRectangle.stop();
813 }
814 }
816 onMouseMoved: {
817 mouseNeverMoved = false;
818 cursor.opacity = 1;
819 }
821 Behavior on opacity { UbuntuNumberAnimation {} }
822 }
824 // non-visual objects
825 KeymapSwitcher {
826 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
827 }
828 BrightnessControl {}
830 Rectangle {
831 id: shutdownFadeOutRectangle
832 z: cursor.z + 1
833 enabled: false
834 visible: false
835 color: "black"
836 anchors.fill: parent
837 opacity: 0.0
838 NumberAnimation on opacity {
839 id: shutdownFadeOut
840 from: 0.0
841 to: 1.0
842 onStopped: {
843 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
844 DBusUnitySessionService.shutdown();
845 }
846 }
847 }
848 }