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