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