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