Unity 8
DesktopStage.qml
1 /*
2  * Copyright (C) 2014-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 Ubuntu.Components 1.3
19 import Unity.Application 0.1
20 import "../Components/PanelState"
21 import "../Components"
22 import Utils 0.1
23 import Ubuntu.Gestures 0.1
24 import GlobalShortcut 1.0
25 
26 AbstractStage {
27  id: root
28  anchors.fill: parent
29  paintBackground: false
30 
31  // functions to be called from outside
32  function updateFocusedAppOrientation() { /* TODO */ }
33  function updateFocusedAppOrientationAnimated() { /* TODO */}
34  function pushRightEdge(amount) {
35  if (spread.state === "") {
36  edgeBarrier.push(amount);
37  }
38  }
39 
40  // Used by TutorialRight
41  property bool spreadShown: spread.state == "altTab"
42 
43  mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
44 
45  // application windows never rotate independently
46  mainAppWindowOrientationAngle: shellOrientationAngle
47 
48  orientationChangesEnabled: true
49 
50  GlobalShortcut {
51  id: closeWindowShortcut
52  shortcut: Qt.AltModifier|Qt.Key_F4
53  onTriggered: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
54  active: priv.focusedAppDelegate !== null
55  }
56 
57  GlobalShortcut {
58  id: showSpreadShortcut
59  shortcut: Qt.MetaModifier|Qt.Key_W
60  onTriggered: spread.state = "altTab"
61  }
62 
63  GlobalShortcut {
64  id: minimizeAllShortcut
65  shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
66  onTriggered: priv.minimizeAllWindows()
67  }
68 
69  GlobalShortcut {
70  id: maximizeWindowShortcut
71  shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
72  onTriggered: priv.focusedAppDelegate.maximize()
73  active: priv.focusedAppDelegate !== null
74  }
75 
76  GlobalShortcut {
77  id: maximizeWindowLeftShortcut
78  shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
79  onTriggered: priv.focusedAppDelegate.maximizeLeft()
80  active: priv.focusedAppDelegate !== null
81  }
82 
83  GlobalShortcut {
84  id: maximizeWindowRightShortcut
85  shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
86  onTriggered: priv.focusedAppDelegate.maximizeRight()
87  active: priv.focusedAppDelegate !== null
88  }
89 
90  GlobalShortcut {
91  id: minimizeRestoreShortcut
92  shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
93  onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight ||
94  priv.focusedAppDelegate.maximizedHorizontally || priv.focusedAppDelegate.maximizedVertically
95  ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
96  active: priv.focusedAppDelegate !== null
97  }
98 
99  GlobalShortcut {
100  shortcut: Qt.AltModifier|Qt.Key_Print
101  onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
102  active: priv.focusedAppDelegate !== null
103  }
104 
105  Connections {
106  target: root.topLevelSurfaceList
107  onCountChanged: {
108  if (spread.state == "altTab") {
109  spread.cancel();
110  }
111  }
112  }
113 
114  QtObject {
115  id: priv
116  objectName: "DesktopStagePrivate"
117 
118  property var focusedAppDelegate: null
119  onFocusedAppDelegateChanged: {
120  if (spread.state == "altTab") {
121  spread.state = "";
122  }
123  }
124 
125  property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
126 
127  function updateForegroundMaximizedApp() {
128  var found = false;
129  for (var i = 0; i < appRepeater.count && !found; i++) {
130  var item = appRepeater.itemAt(i);
131  if (item && item.visuallyMaximized) {
132  foregroundMaximizedAppDelegate = item;
133  found = true;
134  }
135  }
136  if (!found) {
137  foregroundMaximizedAppDelegate = null;
138  }
139  }
140 
141  function minimizeAllWindows() {
142  for (var i = 0; i < appRepeater.count; i++) {
143  var appDelegate = appRepeater.itemAt(i);
144  if (appDelegate && !appDelegate.minimized) {
145  appDelegate.minimize();
146  }
147  }
148  }
149 
150  function focusNext() {
151  for (var i = 0; i < appRepeater.count; i++) {
152  var appDelegate = appRepeater.itemAt(i);
153  if (appDelegate && !appDelegate.minimized) {
154  appDelegate.focus = true;
155  return;
156  }
157  }
158  }
159  }
160 
161  Connections {
162  target: PanelState
163  onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
164  onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
165  onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
166  onFocusMaximizedApp: {
167  if (priv.foregroundMaximizedAppDelegate) {
168  priv.foregroundMaximizedAppDelegate.focus = true;
169  }
170  }
171  }
172 
173  Binding {
174  target: PanelState
175  property: "buttonsVisible"
176  value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
177  && spread.state == ""
178  }
179 
180  Binding {
181  target: PanelState
182  property: "title"
183  value: {
184  if (priv.focusedAppDelegate !== null && spread.state == "") {
185  if (priv.focusedAppDelegate.maximized)
186  return priv.focusedAppDelegate.title
187  else
188  return priv.focusedAppDelegate.appName
189  }
190  return ""
191  }
192  when: priv.focusedAppDelegate
193  }
194 
195  Binding {
196  target: PanelState
197  property: "dropShadow"
198  value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null
199  }
200 
201  Binding {
202  target: PanelState
203  property: "closeButtonShown"
204  value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && priv.focusedAppDelegate.application.appId !== "unity8-dash"
205  }
206 
207  Component.onDestruction: {
208  PanelState.title = "";
209  PanelState.buttonsVisible = false;
210  PanelState.dropShadow = false;
211  }
212 
213  Instantiator {
214  model: root.applicationManager
215  delegate: Binding {
216  target: model.application
217  property: "requestedState"
218 
219  // TODO: figure out some lifecycle policy, like suspending minimized apps
220  // if running on a tablet or something.
221  // TODO: If the device has a dozen suspended apps because it was running
222  // in staged mode, when it switches to Windowed mode it will suddenly
223  // resume all those apps at once. We might want to avoid that.
224  value: ApplicationInfoInterface.RequestedRunning // Always running for now
225  }
226  }
227 
228  Binding {
229  target: MirFocusController
230  property: "focusedSurface"
231  value: priv.focusedAppDelegate ? priv.focusedAppDelegate.surface : null
232  when: !appRepeater.startingUp && root.parent
233  }
234 
235  FocusScope {
236  id: appContainer
237  objectName: "appContainer"
238  anchors.fill: parent
239  focus: spread.state !== "altTab"
240 
241  CrossFadeImage {
242  id: wallpaper
243  anchors.fill: parent
244  source: root.background
245  sourceSize { height: root.height; width: root.width }
246  fillMode: Image.PreserveAspectCrop
247  }
248 
249  TopLevelSurfaceRepeater {
250  id: appRepeater
251  model: topLevelSurfaceList
252  objectName: "appRepeater"
253 
254  delegate: FocusScope {
255  id: appDelegate
256  objectName: "appDelegate_" + model.id
257  // z might be overriden in some cases by effects, but we need z ordering
258  // to calculate occlusion detection
259  property int normalZ: topLevelSurfaceList.count - index
260  onNormalZChanged: {
261  if (visuallyMaximized) {
262  priv.updateForegroundMaximizedApp();
263  }
264  }
265  z: normalZ
266  x: requestedX // may be overridden in some states. Do not directly write to this.
267  y: requestedY // may be overridden in some states. Do not directly write to this.
268  property real requestedX: priv.focusedAppDelegate ? priv.focusedAppDelegate.x + units.gu(3) : (normalZ - 1) * units.gu(3)
269  property real requestedY: priv.focusedAppDelegate ? priv.focusedAppDelegate.y + units.gu(3) : normalZ * units.gu(3)
270 
271  Binding {
272  target: appDelegate
273  property: "y"
274  value: appDelegate.requestedY -
275  Math.min(appDelegate.requestedY - PanelState.panelHeight,
276  Math.max(0, UbuntuKeyboardInfo.height - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
277  when: root.oskEnabled && appDelegate.focus && appDelegate.state == "normal"
278  && SurfaceManager.inputMethodSurface
279  && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
280  && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
281 
282  }
283 
284  width: decoratedWindow.width
285  height: decoratedWindow.height
286 
287  Connections {
288  target: root
289  onShellOrientationAngleChanged: {
290  // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
291  if (application && application.rotatesWindowContents) {
292  if (state == "normal") {
293  var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
294  angleDiff = (360 + angleDiff) % 360;
295  if (angleDiff === 90 || angleDiff === 270) {
296  var aux = decoratedWindow.requestedHeight;
297  decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.visibleDecorationHeight;
298  decoratedWindow.requestedWidth = aux - decoratedWindow.visibleDecorationHeight;
299  }
300  }
301  decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
302  } else {
303  decoratedWindow.surfaceOrientationAngle = 0;
304  }
305  }
306  }
307 
308  readonly property alias application: decoratedWindow.application
309  readonly property alias minimumWidth: decoratedWindow.minimumWidth
310  readonly property alias minimumHeight: decoratedWindow.minimumHeight
311  readonly property alias maximumWidth: decoratedWindow.maximumWidth
312  readonly property alias maximumHeight: decoratedWindow.maximumHeight
313  readonly property alias widthIncrement: decoratedWindow.widthIncrement
314  readonly property alias heightIncrement: decoratedWindow.heightIncrement
315  property int requestedWidth: -1
316  property int requestedHeight: -1
317 
318  readonly property bool maximized: windowState & WindowStateStorage.WindowStateMaximized
319  readonly property bool maximizedLeft: windowState & WindowStateStorage.WindowStateMaximizedLeft
320  readonly property bool maximizedRight: windowState & WindowStateStorage.WindowStateMaximizedRight
321  readonly property bool maximizedHorizontally: windowState & WindowStateStorage.WindowStateMaximizedHorizontally
322  readonly property bool maximizedVertically: windowState & WindowStateStorage.WindowStateMaximizedVertically
323  readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
324  readonly property alias fullscreen: decoratedWindow.fullscreen
325 
326  property int windowState: WindowStateStorage.WindowStateNormal
327  property bool animationsEnabled: true
328  property alias title: decoratedWindow.title
329  readonly property string appName: model.application ? model.application.name : ""
330  property bool visuallyMaximized: false
331  property bool visuallyMinimized: false
332 
333  readonly property var surface: model.surface
334  readonly property alias resizeArea: resizeArea
335 
336  function claimFocus() {
337  if (spread.state == "altTab") {
338  spread.cancel();
339  }
340  appDelegate.restore();
341  }
342  Connections {
343  target: model.surface
344  onFocusRequested: claimFocus();
345  }
346  Connections {
347  target: model.application
348  onFocusRequested: {
349  if (!model.surface) {
350  // when an app has no surfaces, we assume there's only one entry representing it:
351  // this delegate.
352  claimFocus();
353  } else {
354  // if the application has surfaces, focus request should be at surface-level.
355  }
356  }
357  }
358 
359  onFocusChanged: {
360  if (appRepeater.startingUp)
361  return;
362 
363  if (focus) {
364  // If we're orphan (!parent) it means this stage is no longer the current one
365  // and will be deleted shortly. So we should no longer have a say over the model
366  if (root.parent) {
367  topLevelSurfaceList.raiseId(model.id);
368  }
369 
370  priv.focusedAppDelegate = appDelegate;
371  } else if (!focus && priv.focusedAppDelegate === appDelegate) {
372  priv.focusedAppDelegate = null;
373  // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null
374  MirFocusController.focusedSurface = null;
375  }
376  }
377  Component.onCompleted: {
378  if (application && application.rotatesWindowContents) {
379  decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
380  } else {
381  decoratedWindow.surfaceOrientationAngle = 0;
382  }
383 
384  // NB: We're differentiating if this delegate was created in response to a new entry in the model
385  // or if the Repeater is just populating itself with delegates to match the model it received.
386  if (!appRepeater.startingUp) {
387  // a top level window is always the focused one when it first appears, unfocusing
388  // any preexisting one
389  focus = true;
390  }
391  }
392  Component.onDestruction: {
393  if (!root.parent) {
394  // This stage is about to be destroyed. Don't mess up with the model at this point
395  return;
396  }
397 
398  if (visuallyMaximized) {
399  priv.updateForegroundMaximizedApp();
400  }
401 
402  if (focus) {
403  // focus some other window
404  for (var i = 0; i < appRepeater.count; i++) {
405  var appDelegate = appRepeater.itemAt(i);
406  if (appDelegate && !appDelegate.minimized && i != index) {
407  appDelegate.focus = true;
408  return;
409  }
410  }
411  }
412  }
413 
414  onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
415 
416  visible: (
417  !visuallyMinimized
418  && !greeter.fullyShown
419  && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
420  )
421  || decoratedWindow.fullscreen
422  || (spread.state == "altTab" && index === spread.highlightedIndex)
423 
424  function close() {
425  model.surface.close();
426  }
427 
428  function maximize(animated) {
429  animationsEnabled = (animated === undefined) || animated;
430  windowState = WindowStateStorage.WindowStateMaximized;
431  }
432  function maximizeLeft(animated) {
433  animationsEnabled = (animated === undefined) || animated;
434  windowState = WindowStateStorage.WindowStateMaximizedLeft;
435  }
436  function maximizeRight(animated) {
437  animationsEnabled = (animated === undefined) || animated;
438  windowState = WindowStateStorage.WindowStateMaximizedRight;
439  }
440  function maximizeHorizontally(animated) {
441  animationsEnabled = (animated === undefined) || animated;
442  windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
443  }
444  function maximizeVertically(animated) {
445  animationsEnabled = (animated === undefined) || animated;
446  windowState = WindowStateStorage.WindowStateMaximizedVertically;
447  }
448  function minimize(animated) {
449  animationsEnabled = (animated === undefined) || animated;
450  windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
451  }
452  function restoreFromMaximized(animated) {
453  animationsEnabled = (animated === undefined) || animated;
454  windowState = WindowStateStorage.WindowStateNormal;
455  }
456  function restore(animated) {
457  animationsEnabled = (animated === undefined) || animated;
458  windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
459  if (maximized)
460  maximize();
461  else if (maximizedLeft)
462  maximizeLeft();
463  else if (maximizedRight)
464  maximizeRight();
465  else if (maximizedHorizontally)
466  maximizeHorizontally();
467  else if (maximizedVertically)
468  maximizeVertically();
469 
470  focus = true;
471  }
472 
473  function playFocusAnimation() {
474  focusAnimation.start()
475  }
476 
477  UbuntuNumberAnimation {
478  id: focusAnimation
479  target: appDelegate
480  property: "scale"
481  from: 0.98
482  to: 1
483  duration: UbuntuAnimation.SnapDuration
484  }
485 
486  states: [
487  State {
488  name: "fullscreen"; when: decoratedWindow.fullscreen && !appDelegate.minimized
489  PropertyChanges {
490  target: appDelegate;
491  x: rotation == 0 ? 0 : (parent.width - width) / 2 + (shellOrientationAngle == 90 ? -PanelState.panelHeight : PanelState.panelHeight)
492  y: rotation == 0 ? -PanelState.panelHeight : (parent.height - height) / 2
493  requestedWidth: appContainer.width;
494  requestedHeight: appContainer.height;
495  }
496  },
497  State {
498  name: "normal";
499  when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
500  PropertyChanges {
501  target: appDelegate;
502  visuallyMinimized: false;
503  visuallyMaximized: false
504  }
505  },
506  State {
507  name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
508  PropertyChanges {
509  target: appDelegate;
510  x: root.leftMargin;
511  y: 0;
512  visuallyMinimized: false;
513  visuallyMaximized: true
514  }
515  PropertyChanges {
516  target: decoratedWindow
517  requestedWidth: appContainer.width - root.leftMargin;
518  requestedHeight: appContainer.height;
519  }
520  },
521  State {
522  name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
523  PropertyChanges {
524  target: appDelegate
525  x: root.leftMargin
526  y: PanelState.panelHeight
527  }
528  PropertyChanges {
529  target: decoratedWindow
530  requestedWidth: (appContainer.width - root.leftMargin)/2
531  requestedHeight: appContainer.height - PanelState.panelHeight
532  }
533  },
534  State {
535  name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
536  PropertyChanges {
537  target: appDelegate;
538  x: (appContainer.width + root.leftMargin)/2
539  y: PanelState.panelHeight
540  }
541  PropertyChanges {
542  target: decoratedWindow
543  requestedWidth: (appContainer.width - root.leftMargin)/2
544  requestedHeight: appContainer.height - PanelState.panelHeight
545  }
546  },
547  State {
548  name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
549  PropertyChanges { target: appDelegate; x: root.leftMargin }
550  PropertyChanges { target: decoratedWindow; requestedWidth: appContainer.width - root.leftMargin }
551  },
552  State {
553  name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
554  PropertyChanges { target: appDelegate; y: PanelState.panelHeight }
555  PropertyChanges { target: decoratedWindow; requestedHeight: appContainer.height - PanelState.panelHeight }
556  },
557  State {
558  name: "minimized"; when: appDelegate.minimized
559  PropertyChanges {
560  target: appDelegate;
561  x: -appDelegate.width / 2;
562  scale: units.gu(5) / appDelegate.width;
563  opacity: 0;
564  visuallyMinimized: true;
565  visuallyMaximized: false
566  }
567  }
568  ]
569  transitions: [
570  Transition {
571  to: "normal"
572  enabled: appDelegate.animationsEnabled
573  PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
574  UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
575  UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
576  },
577  Transition {
578  to: "minimized"
579  enabled: appDelegate.animationsEnabled
580  PropertyAction { target: appDelegate; property: "visuallyMaximized" }
581  SequentialAnimation {
582  ParallelAnimation {
583  UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
584  UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
585  }
586  PropertyAction { target: appDelegate; property: "visuallyMinimized" }
587  ScriptAction {
588  script: {
589  if (appDelegate.minimized) {
590  appDelegate.focus = false;
591  priv.focusNext();
592  }
593  }
594  }
595  }
596  },
597  Transition {
598  to: "*" //maximized and fullscreen
599  enabled: appDelegate.animationsEnabled
600  PropertyAction { target: appDelegate; property: "visuallyMinimized" }
601  SequentialAnimation {
602  ParallelAnimation {
603  UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
604  UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
605  }
606  PropertyAction { target: appDelegate; property: "visuallyMaximized" }
607  }
608  }
609  ]
610 
611  Binding {
612  id: previewBinding
613  target: appDelegate
614  property: "z"
615  value: topLevelSurfaceList.count + 1
616  when: index == spread.highlightedIndex && spread.ready
617  }
618 
619  Binding {
620  target: PanelState
621  property: "buttonsAlwaysVisible"
622  value: appDelegate && appDelegate.maximized && touchControls.overlayShown
623  }
624 
625  WindowResizeArea {
626  id: resizeArea
627  objectName: "windowResizeArea"
628 
629  // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
630  anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
631 
632  target: appDelegate
633  minWidth: units.gu(10)
634  minHeight: units.gu(10)
635  borderThickness: units.gu(2)
636  windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
637  screenWidth: appContainer.width
638  screenHeight: appContainer.height
639  leftMargin: root.leftMargin
640 
641  onPressed: { appDelegate.focus = true; }
642 
643  Component.onCompleted: {
644  loadWindowState();
645  }
646 
647  property bool saveStateOnDestruction: true
648  Connections {
649  target: root
650  onStageAboutToBeUnloaded: {
651  resizeArea.saveWindowState();
652  resizeArea.saveStateOnDestruction = false;
653  fullscreenPolicy.active = false;
654  }
655  }
656  Component.onDestruction: {
657  if (saveStateOnDestruction) {
658  saveWindowState();
659  }
660  }
661  }
662 
663  DecoratedWindow {
664  id: decoratedWindow
665  objectName: "decoratedWindow"
666  anchors.left: appDelegate.left
667  anchors.top: appDelegate.top
668  application: model.application
669  surface: model.surface
670  active: appDelegate.focus
671  focus: true
672  maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
673  (maximumHeight == 0 || maximumHeight >= appContainer.height)
674  overlayShown: touchControls.overlayShown
675 
676  requestedWidth: appDelegate.requestedWidth
677  requestedHeight: appDelegate.requestedHeight
678 
679  onCloseClicked: { appDelegate.close(); }
680  onMaximizeClicked: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
681  || appDelegate.maximizedHorizontally || appDelegate.maximizedVertically
682  ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
683  onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
684  onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
685  onMinimizeClicked: appDelegate.minimize()
686  onDecorationPressed: { appDelegate.focus = true; }
687  }
688 
689  WindowControlsOverlay {
690  id: touchControls
691  anchors.fill: appDelegate
692  target: appDelegate
693  }
694 
695  WindowedFullscreenPolicy {
696  id: fullscreenPolicy
697  active: true
698  surface: model.surface
699  }
700  }
701  }
702  }
703 
704  EdgeBarrier {
705  id: edgeBarrier
706 
707  // NB: it does its own positioning according to the specified edge
708  edge: Qt.RightEdge
709 
710  onPassed: { spread.show(); }
711  material: Component {
712  Item {
713  Rectangle {
714  width: parent.height
715  height: parent.width
716  rotation: 90
717  anchors.centerIn: parent
718  gradient: Gradient {
719  GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
720  GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
721  }
722  }
723  }
724  }
725  }
726 
727  SwipeArea {
728  direction: Direction.Leftwards
729  anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
730  width: units.gu(1)
731  onDraggingChanged: { if (dragging) { spread.show(); } }
732  }
733 
734  DesktopSpread {
735  id: spread
736  objectName: "spread"
737  anchors.fill: appContainer
738  workspace: appContainer
739  focus: state == "altTab"
740  altTabPressed: root.altTabPressed
741 
742  onPlayFocusAnimation: {
743  appRepeater.itemAt(index).playFocusAnimation();
744  }
745  }
746 }