Unity 8
Shell.qml
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  */
16 
17 import QtQuick 2.4
18 import QtQuick.Window 2.2
19 import AccountsService 0.1
20 import Unity.Application 0.1
21 import Ubuntu.Components 1.3
22 import Ubuntu.Components.Popups 1.3
23 import Ubuntu.Gestures 0.1
24 import Ubuntu.Telephony 0.1 as Telephony
25 import Unity.Connectivity 0.1
26 import Unity.Launcher 0.1
27 import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28 import GSettings 1.0
29 import ImageCache 0.1
30 import Utils 0.1
31 import Powerd 0.1
32 import SessionBroadcast 0.1
33 import "Greeter"
34 import "Launcher"
35 import "Panel"
36 import "Components"
37 import "Notifications"
38 import "Stage"
39 import "Tutorial"
40 import "Wizard"
41 import Unity.Notifications 1.0 as NotificationBackend
42 import Unity.Session 0.1
43 import Unity.DashCommunicator 0.1
44 import Unity.Indicators 0.1 as Indicators
45 import Cursor 1.1
46 import WindowManager 1.0
47 
48 
49 StyledItem {
50  id: shell
51 
52  theme.name: "Ubuntu.Components.Themes.SuruDark"
53 
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
72  property bool hasTouchscreen: false
73 
74  // to be read from outside
75  readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
76 
77  readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78  && stage.orientationChangesEnabled
79  && (!greeter || !greeter.animating)
80 
81  readonly property bool showingGreeter: greeter && greeter.shown
82 
83  property bool startingUp: true
84  Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
85 
86  property int supportedOrientations: {
87  if (startingUp) {
88  // Ensure we don't rotate during start up
89  return Qt.PrimaryOrientation;
90  } else if (showingGreeter || notifications.topmostIsFullscreen) {
91  return Qt.PrimaryOrientation;
92  } else {
93  return shell.orientations.map(stage.supportedOrientations);
94  }
95  }
96 
97  readonly property var mainApp: stage.mainApp
98 
99  onMainAppChanged: {
100  if (mainApp) {
101  _onMainAppChanged(mainApp.appId);
102  }
103  }
104  Connections {
105  target: ApplicationManager
106  onFocusRequested: {
107  if (shell.mainApp && shell.mainApp.appId === appId) {
108  _onMainAppChanged(appId);
109  }
110  }
111  }
112  function _onMainAppChanged(appId) {
113  if (wizard.active && appId != "") {
114  // If this happens on first boot, we may be in the
115  // wizard while receiving a call. But a call is more
116  // important than the wizard so just bail out of it.
117  wizard.hide();
118  }
119 
120  if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
121  // If we are in the middle of a call, make dialer lockedApp and show it.
122  // This can happen if user backs out of dialer back to greeter, then
123  // launches dialer again.
124  greeter.lockedApp = appId;
125  }
126  greeter.notifyAppFocusRequested(appId);
127 
128  panel.indicators.hide();
129  launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
130  }
131 
132  // For autopilot consumption
133  readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
134 
135  // Note when greeter is waiting on PAM, so that we can disable edges until
136  // we know which user data to show and whether the session is locked.
137  readonly property bool waitingOnGreeter: greeter && greeter.waiting
138 
139  property real edgeSize: units.gu(settings.edgeDragWidth)
140 
141  WallpaperResolver {
142  id: wallpaperResolver
143  objectName: "wallpaperResolver"
144 
145  readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
146  readonly property bool hasCustomBackground: background != defaultBackground
147 
148  // Use a cached version of the scaled-down wallpaper (as sometimes the
149  // image can be quite big compared to the device size, including for
150  // our default wallpaper). We use a name=wallpaper argument here to
151  // make sure we don't litter our cache with lots of scaled images. We
152  // only need to bother caching one at a time.
153  readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
154 
155  GSettings {
156  id: backgroundSettings
157  schema.id: "org.gnome.desktop.background"
158  }
159 
160  candidates: [
161  AccountsService.backgroundFile,
162  backgroundSettings.pictureUri,
163  defaultBackground
164  ]
165  }
166 
167  readonly property alias greeter: greeterLoader.item
168 
169  function activateApplication(appId) {
170  // Either open the app in our own session, or -- if we're acting as a
171  // greeter -- ask the user's session to open it for us.
172  if (shell.mode === "greeter") {
173  activateURL("application:///" + appId + ".desktop");
174  } else {
175  startApp(appId);
176  }
177  }
178 
179  function activateURL(url) {
180  SessionBroadcast.requestUrlStart(AccountsService.user, url);
181  greeter.notifyUserRequestedApp();
182  panel.indicators.hide();
183  }
184 
185  function startApp(appId) {
186  if (ApplicationManager.findApplication(appId)) {
187  ApplicationManager.requestFocusApplication(appId);
188  } else {
189  ApplicationManager.startApplication(appId);
190  }
191  }
192 
193  function startLockedApp(app) {
194  if (greeter.locked) {
195  greeter.lockedApp = app;
196  }
197  startApp(app); // locked apps are always in our same session
198  }
199 
200  Binding {
201  target: LauncherModel
202  property: "applicationManager"
203  value: ApplicationManager
204  }
205 
206  Component.onCompleted: {
207  finishStartUpTimer.start();
208  }
209 
210  VolumeControl {
211  id: volumeControl
212  }
213 
214  DashCommunicator {
215  id: dash
216  objectName: "dashCommunicator"
217  }
218 
219  PhysicalKeysMapper {
220  id: physicalKeysMapper
221  objectName: "physicalKeysMapper"
222 
223  onPowerKeyLongPressed: dialogs.showPowerDialog();
224  onVolumeDownTriggered: volumeControl.volumeDown();
225  onVolumeUpTriggered: volumeControl.volumeUp();
226  onScreenshotTriggered: itemGrabber.capture(shell);
227  }
228 
229  GlobalShortcut {
230  // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
231  }
232 
233  WindowInputFilter {
234  id: inputFilter
235  Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
236  Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
237  }
238 
239  WindowInputMonitor {
240  objectName: "windowInputMonitor"
241  onHomeKeyActivated: {
242  // Ignore when greeter is active, to avoid pocket presses
243  if (!greeter.active) {
244  launcher.openDrawer(false);
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  }
256 
257  AvailableDesktopArea {
258  id: availableDesktopAreaItem
259  anchors.fill: parent
260  anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
261  anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
262  }
263 
264  GSettings {
265  id: settings
266  schema.id: "com.canonical.Unity8"
267  }
268 
269  Item {
270  id: stages
271  objectName: "stages"
272  width: parent.width
273  height: parent.height
274 
275  SurfaceManager {
276  id: surfaceMan
277  objectName: "surfaceManager"
278  }
279  TopLevelWindowModel {
280  id: topLevelSurfaceList
281  objectName: "topLevelSurfaceList"
282  applicationManager: ApplicationManager // it's a singleton
283  surfaceManager: surfaceMan
284  }
285 
286  Stage {
287  id: stage
288  objectName: "stage"
289  anchors.fill: parent
290  focus: true
291 
292  dragAreaWidth: shell.edgeSize
293  background: wallpaperResolver.background
294 
295  applicationManager: ApplicationManager
296  topLevelSurfaceList: topLevelSurfaceList
297  inputMethodRect: inputMethod.visibleRect
298  rightEdgePushProgress: rightEdgeBarrier.progress
299  availableDesktopArea: availableDesktopAreaItem
300 
301  property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
302  ? "phone"
303  : shell.usageScenario
304 
305  mode: usageScenario == "phone" ? "staged"
306  : usageScenario == "tablet" ? "stagedWithSideStage"
307  : "windowed"
308 
309  shellOrientation: shell.orientation
310  shellOrientationAngle: shell.orientationAngle
311  orientations: shell.orientations
312  nativeWidth: shell.nativeWidth
313  nativeHeight: shell.nativeHeight
314 
315  interactive: (!greeter || !greeter.shown)
316  && panel.indicators.fullyClosed
317  && !notifications.useModal
318 
319  onInteractiveChanged: { if (interactive) { focus = true; } }
320 
321  suspended: greeter.shown
322  altTabPressed: physicalKeysMapper.altTabPressed
323  oskEnabled: shell.oskEnabled
324  spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
325 
326  onSpreadShownChanged: {
327  panel.indicators.hide();
328  panel.applicationMenus.hide();
329  }
330  }
331 
332  TouchGestureArea {
333  anchors.fill: stage
334 
335  minimumTouchPoints: 4
336  maximumTouchPoints: minimumTouchPoints
337 
338  readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
339  touchPoints.length >= minimumTouchPoints &&
340  touchPoints.length <= maximumTouchPoints
341  property bool wasPressed: false
342 
343  onRecognisedPressChanged: {
344  if (recognisedPress) {
345  wasPressed = true;
346  }
347  }
348 
349  onStatusChanged: {
350  if (status !== TouchGestureArea.Recognized) {
351  if (status === TouchGestureArea.WaitingForTouch) {
352  if (wasPressed && !dragging) {
353  launcher.openDrawer(true);
354  }
355  }
356  wasPressed = false;
357  }
358  }
359  }
360  }
361 
362  InputMethod {
363  id: inputMethod
364  objectName: "inputMethod"
365  surface: topLevelSurfaceList.inputMethodSurface
366  anchors {
367  fill: parent
368  topMargin: panel.panelHeight
369  leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
370  }
371  z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
372  }
373 
374  Loader {
375  id: greeterLoader
376  objectName: "greeterLoader"
377  anchors.fill: parent
378  anchors.topMargin: panel.panelHeight
379  sourceComponent: shell.mode != "shell" ? integratedGreeter :
380  Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
381  onLoaded: {
382  item.objectName = "greeter"
383  }
384  property bool openDrawerAfterUnlock: false
385  Connections {
386  target: greeter
387  onActiveChanged: {
388  if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
389  launcher.openDrawer(false);
390  greeterLoader.openDrawerAfterUnlock = false;
391  }
392  }
393  }
394  }
395 
396  Component {
397  id: integratedGreeter
398  Greeter {
399 
400  enabled: panel.indicators.fullyClosed // hides OSK when panel is open
401  hides: [launcher, panel.indicators, panel.applicationMenus]
402  tabletMode: shell.usageScenario != "phone"
403  forcedUnlock: wizard.active || shell.mode === "full-shell"
404  background: wallpaperResolver.cachedBackground
405  hasCustomBackground: wallpaperResolver.hasCustomBackground
406  allowFingerprint: !dialogs.hasActiveDialog &&
407  !notifications.topmostIsFullscreen &&
408  !panel.indicators.shown
409 
410  // avoid overlapping with Launcher's edge drag area
411  // FIXME: Fix TouchRegistry & friends and remove this workaround
412  // Issue involves launcher's DDA getting disabled on a long
413  // left-edge drag
414  dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
415 
416  onSessionStarted: {
417  launcher.hide();
418  }
419 
420  onTease: {
421  if (!tutorial.running) {
422  launcher.tease();
423  }
424  }
425 
426  onEmergencyCall: startLockedApp("dialer-app")
427  }
428  }
429 
430  Timer {
431  // See powerConnection for why this is useful
432  id: showGreeterDelayed
433  interval: 1
434  onTriggered: {
435  // Go through the dbus service, because it has checks for whether
436  // we are even allowed to lock or not.
437  DBusUnitySessionService.PromptLock();
438  }
439  }
440 
441  Connections {
442  id: callConnection
443  target: callManager
444 
445  onHasCallsChanged: {
446  if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
447  // We just received an incoming call while locked. The
448  // indicator will have already launched dialer-app for us, but
449  // there is a race between "hasCalls" changing and the dialer
450  // starting up. So in case we lose that race, we'll start/
451  // focus the dialer ourselves here too. Even if the indicator
452  // didn't launch the dialer for some reason (or maybe a call
453  // started via some other means), if an active call is
454  // happening, we want to be in the dialer.
455  startLockedApp("dialer-app")
456  }
457  }
458  }
459 
460  Connections {
461  id: powerConnection
462  target: Powerd
463 
464  onStatusChanged: {
465  if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
466  !callManager.hasCalls && !wizard.active) {
467  // We don't want to simply call greeter.showNow() here, because
468  // that will take too long. Qt will delay button event
469  // handling until the greeter is done loading and may think the
470  // user held down the power button the whole time, leading to a
471  // power dialog being shown. Instead, delay showing the
472  // greeter until we've finished handling the event. We could
473  // make the greeter load asynchronously instead, but that
474  // introduces a whole host of timing issues, especially with
475  // its animations. So this is simpler.
476  showGreeterDelayed.start();
477  }
478  }
479  }
480 
481  function showHome() {
482  greeter.notifyUserRequestedApp();
483 
484  if (shell.mode === "greeter") {
485  SessionBroadcast.requestHomeShown(AccountsService.user);
486  } else {
487  if (!greeter.active) {
488  launcher.openDrawer(false);
489  } else {
490  greeterLoader.openDrawerAfterUnlock = true;
491  }
492  }
493  }
494 
495  Item {
496  id: overlay
497  z: 10
498 
499  anchors.fill: parent
500 
501  Panel {
502  id: panel
503  objectName: "panel"
504  anchors.fill: parent //because this draws indicator menus
505 
506  mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
507  minimizedPanelHeight: units.gu(3)
508  expandedPanelHeight: units.gu(7)
509  indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
510  applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
511  applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
512 
513  indicators {
514  hides: [launcher]
515  available: tutorial.panelEnabled
516  && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
517  && (!greeter || !greeter.hasLockedApp)
518  && !shell.waitingOnGreeter
519  && settings.enableIndicatorMenu
520 
521  model: Indicators.IndicatorsModel {
522  // tablet and phone both use the same profile
523  // FIXME: use just "phone" for greeter too, but first fix
524  // greeter app launching to either load the app inside the
525  // greeter or tell the session to load the app. This will
526  // involve taking the url-dispatcher dbus name and using
527  // SessionBroadcast to tell the session.
528  profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
529  Component.onCompleted: load();
530  }
531  }
532 
533  applicationMenus {
534  hides: [launcher]
535  available: (!greeter || !greeter.shown)
536  && !shell.waitingOnGreeter
537  && !stage.spreadShown
538  }
539 
540  readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
541  ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
542  : false
543  fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
544  || greeter.hasLockedApp
545  greeterShown: greeter && greeter.shown
546  hasKeyboard: shell.hasKeyboard
547  }
548 
549  Launcher {
550  id: launcher
551  objectName: "launcher"
552 
553  anchors.top: parent.top
554  anchors.topMargin: inverted ? 0 : panel.panelHeight
555  anchors.bottom: parent.bottom
556  width: parent.width
557  dragAreaWidth: shell.edgeSize
558  available: tutorial.launcherEnabled
559  && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
560  && !greeter.hasLockedApp
561  && !shell.waitingOnGreeter
562  && settings.enableLauncher
563  inverted: shell.usageScenario !== "desktop"
564  superPressed: physicalKeysMapper.superPressed
565  superTabPressed: physicalKeysMapper.superTabPressed
566  panelWidth: units.gu(settings.launcherWidth)
567  lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
568  blurSource: greeter.shown ? greeter : stages
569  topPanelHeight: panel.panelHeight
570  drawerEnabled: !greeter.active
571  privateMode: greeter.active
572 
573  onShowDashHome: showHome()
574  onLauncherApplicationSelected: {
575  greeter.notifyUserRequestedApp();
576  shell.activateApplication(appId);
577  }
578  onShownChanged: {
579  if (shown) {
580  panel.indicators.hide();
581  panel.applicationMenus.hide();
582  }
583  }
584  onDrawerShownChanged: {
585  if (drawerShown) {
586  panel.indicators.hide();
587  panel.applicationMenus.hide();
588  }
589  }
590  onFocusChanged: {
591  if (!focus) {
592  stage.focus = true;
593  }
594  }
595 
596  GlobalShortcut {
597  shortcut: Qt.MetaModifier | Qt.Key_A
598  onTriggered: {
599  launcher.openDrawer(true);
600  }
601  }
602  GlobalShortcut {
603  shortcut: Qt.AltModifier | Qt.Key_F1
604  onTriggered: {
605  launcher.openForKeyboardNavigation();
606  }
607  }
608  GlobalShortcut {
609  shortcut: Qt.MetaModifier | Qt.Key_0
610  onTriggered: {
611  if (LauncherModel.get(9)) {
612  activateApplication(LauncherModel.get(9).appId);
613  }
614  }
615  }
616  Repeater {
617  model: 9
618  GlobalShortcut {
619  shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
620  onTriggered: {
621  if (LauncherModel.get(index)) {
622  activateApplication(LauncherModel.get(index).appId);
623  }
624  }
625  }
626  }
627  }
628 
629  KeyboardShortcutsOverlay {
630  objectName: "shortcutsOverlay"
631  enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
632  && height < parent.height - padding - panel.panelHeight
633  anchors.centerIn: parent
634  anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
635  anchors.verticalCenterOffset: panel.panelHeight/2
636  visible: opacity > 0
637  opacity: enabled ? 0.95 : 0
638 
639  Behavior on opacity {
640  UbuntuNumberAnimation {}
641  }
642  }
643 
644  Tutorial {
645  id: tutorial
646  objectName: "tutorial"
647  anchors.fill: parent
648 
649  paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
650  || !hasTouchscreen // TODO #1661557 something better for no touchscreen
651  delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
652  inputMethod.visible ||
653  (launcher.shown && !launcher.lockedVisible) ||
654  panel.indicators.shown || stage.rightEdgeDragProgress > 0
655  usageScenario: shell.usageScenario
656  lastInputTimestamp: inputFilter.lastInputTimestamp
657  launcher: launcher
658  panel: panel
659  stage: stage
660  }
661 
662  Wizard {
663  id: wizard
664  objectName: "wizard"
665  anchors.fill: parent
666  deferred: shell.mode === "greeter"
667 
668  function unlockWhenDoneWithWizard() {
669  if (!active) {
670  Connectivity.unlockAllModems();
671  }
672  }
673 
674  Component.onCompleted: unlockWhenDoneWithWizard()
675  onActiveChanged: unlockWhenDoneWithWizard()
676  }
677 
678  MouseArea { // modal notifications prevent interacting with other contents
679  anchors.fill: parent
680  visible: notifications.useModal
681  enabled: visible
682  }
683 
684  Notifications {
685  id: notifications
686 
687  model: NotificationBackend.Model
688  margin: units.gu(1)
689  hasMouse: shell.hasMouse
690  background: wallpaperResolver.cachedBackground
691 
692  y: topmostIsFullscreen ? 0 : panel.panelHeight
693  height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
694 
695  states: [
696  State {
697  name: "narrow"
698  when: overlay.width <= units.gu(60)
699  AnchorChanges {
700  target: notifications
701  anchors.left: parent.left
702  anchors.right: parent.right
703  }
704  },
705  State {
706  name: "wide"
707  when: overlay.width > units.gu(60)
708  AnchorChanges {
709  target: notifications
710  anchors.left: undefined
711  anchors.right: parent.right
712  }
713  PropertyChanges { target: notifications; width: units.gu(38) }
714  }
715  ]
716  }
717 
718  EdgeBarrier {
719  id: rightEdgeBarrier
720  enabled: !greeter.shown
721 
722  // NB: it does its own positioning according to the specified edge
723  edge: Qt.RightEdge
724 
725  onPassed: {
726  panel.indicators.hide()
727  }
728 
729  material: Component {
730  Item {
731  Rectangle {
732  width: parent.height
733  height: parent.width
734  rotation: 90
735  anchors.centerIn: parent
736  gradient: Gradient {
737  GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
738  GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
739  }
740  }
741  }
742  }
743  }
744  }
745 
746  Dialogs {
747  id: dialogs
748  objectName: "dialogs"
749  anchors.fill: parent
750  visible: hasActiveDialog
751  z: overlay.z + 10
752  usageScenario: shell.usageScenario
753  hasKeyboard: shell.hasKeyboard
754  onPowerOffClicked: {
755  shutdownFadeOutRectangle.enabled = true;
756  shutdownFadeOutRectangle.visible = true;
757  shutdownFadeOut.start();
758  }
759  }
760 
761  Connections {
762  target: SessionBroadcast
763  onShowHome: if (shell.mode !== "greeter") showHome()
764  }
765 
766  URLDispatcher {
767  id: urlDispatcher
768  objectName: "urlDispatcher"
769  active: shell.mode === "greeter"
770  onUrlRequested: shell.activateURL(url)
771  }
772 
773  ItemGrabber {
774  id: itemGrabber
775  anchors.fill: parent
776  z: dialogs.z + 10
777  GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
778  Connections {
779  target: stage
780  ignoreUnknownSignals: true
781  onItemSnapshotRequested: itemGrabber.capture(item)
782  }
783  }
784 
785  Timer {
786  id: cursorHidingTimer
787  interval: 3000
788  running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
789  onTriggered: cursor.opacity = 0;
790  }
791 
792  Cursor {
793  id: cursor
794  objectName: "cursor"
795  visible: shell.hasMouse
796  z: itemGrabber.z + 1
797  topBoundaryOffset: panel.panelHeight
798 
799  confiningItem: stage.itemConfiningMouseCursor
800 
801  property bool mouseNeverMoved: true
802  Binding {
803  target: cursor; property: "x"; value: shell.width / 2
804  when: cursor.mouseNeverMoved && cursor.visible
805  }
806  Binding {
807  target: cursor; property: "y"; value: shell.height / 2
808  when: cursor.mouseNeverMoved && cursor.visible
809  }
810 
811  height: units.gu(3)
812 
813  readonly property var previewRectangle: stage.previewRectangle.target &&
814  stage.previewRectangle.target.dragging ?
815  stage.previewRectangle : null
816 
817  onPushedLeftBoundary: {
818  if (buttons === Qt.NoButton) {
819  launcher.pushEdge(amount);
820  } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
821  previewRectangle.maximizeLeft(amount);
822  }
823  }
824 
825  onPushedRightBoundary: {
826  if (buttons === Qt.NoButton) {
827  rightEdgeBarrier.push(amount);
828  } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
829  previewRectangle.maximizeRight(amount);
830  }
831  }
832 
833  onPushedTopBoundary: {
834  if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
835  previewRectangle.maximize(amount);
836  }
837  }
838  onPushedTopLeftCorner: {
839  if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
840  previewRectangle.maximizeTopLeft(amount);
841  }
842  }
843  onPushedTopRightCorner: {
844  if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
845  previewRectangle.maximizeTopRight(amount);
846  }
847  }
848  onPushedBottomLeftCorner: {
849  if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
850  previewRectangle.maximizeBottomLeft(amount);
851  }
852  }
853  onPushedBottomRightCorner: {
854  if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
855  previewRectangle.maximizeBottomRight(amount);
856  }
857  }
858  onPushStopped: {
859  if (previewRectangle) {
860  previewRectangle.stop();
861  }
862  }
863 
864  onMouseMoved: {
865  mouseNeverMoved = false;
866  cursor.opacity = 1;
867  }
868 
869  Behavior on opacity { UbuntuNumberAnimation {} }
870  }
871 
872  // non-visual objects
873  KeymapSwitcher {
874  focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
875  }
876  BrightnessControl {}
877 
878  Rectangle {
879  id: shutdownFadeOutRectangle
880  z: cursor.z + 1
881  enabled: false
882  visible: false
883  color: "black"
884  anchors.fill: parent
885  opacity: 0.0
886  NumberAnimation on opacity {
887  id: shutdownFadeOut
888  from: 0.0
889  to: 1.0
890  onStopped: {
891  if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
892  DBusUnitySessionService.shutdown();
893  }
894  }
895  }
896  }
897 }