Unity 8
DesktopStage.qml
1 /*
2  * Copyright (C) 2014-2015 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  * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17  */
18 
19 import QtQuick 2.4
20 import QtQuick.Layouts 1.1
21 import Ubuntu.Components 1.1
22 import Unity.Application 0.1
23 import "../Components"
24 import "../Components/PanelState"
25 import Utils 0.1
26 import Ubuntu.Gestures 0.1
27 
28 Rectangle {
29  id: root
30  anchors.fill: parent
31 
32  // Controls to be set from outside
33  property int dragAreaWidth // just to comply with the interface shared between stages
34  property real maximizedAppTopMargin
35  property bool interactive
36  property bool spreadEnabled // just to comply with the interface shared between stages
37  property real inverseProgress: 0 // just to comply with the interface shared between stages
38  property int shellOrientationAngle: 0
39  property int shellOrientation
40  property int shellPrimaryOrientation
41  property int nativeOrientation
42  property bool beingResized: false
43  property bool keepDashRunning: true
44  property bool suspended: false
45  property alias background: wallpaper.source
46  property alias altTabPressed: spread.altTabPressed
47 
48  // functions to be called from outside
49  function updateFocusedAppOrientation() { /* TODO */ }
50  function updateFocusedAppOrientationAnimated() { /* TODO */}
51 
52  // To be read from outside
53  readonly property var mainApp: ApplicationManager.focusedApplicationId
54  ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
55  : null
56  property int mainAppWindowOrientationAngle: 0
57  readonly property bool orientationChangesEnabled: false
58 
59  Connections {
60  target: ApplicationManager
61  onApplicationAdded: {
62  if (spread.state == "altTab") {
63  spread.state = "";
64  }
65 
66  ApplicationManager.requestFocusApplication(appId)
67  }
68 
69  onFocusRequested: {
70  var appIndex = priv.indexOf(appId);
71  var appDelegate = appRepeater.itemAt(appIndex);
72  appDelegate.minimized = false;
73  ApplicationManager.focusApplication(appId)
74 
75  if (spread.state == "altTab") {
76  spread.cancel()
77  }
78  }
79  }
80 
81  QtObject {
82  id: priv
83 
84  readonly property string focusedAppId: ApplicationManager.focusedApplicationId
85  readonly property var focusedAppDelegate: {
86  var index = indexOf(focusedAppId);
87  return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
88  }
89 
90  function indexOf(appId) {
91  for (var i = 0; i < ApplicationManager.count; i++) {
92  if (ApplicationManager.get(i).appId == appId) {
93  return i;
94  }
95  }
96  return -1;
97  }
98  }
99 
100  Connections {
101  target: PanelState
102  onClose: {
103  ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
104  }
105  onMinimize: appRepeater.itemAt(0).minimize();
106  onMaximize: appRepeater.itemAt(0).unmaximize();
107  }
108 
109  Binding {
110  target: PanelState
111  property: "buttonsVisible"
112  value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
113  }
114 
115  FocusScope {
116  id: appContainer
117  objectName: "appContainer"
118  anchors.fill: parent
119  focus: spread.state !== "altTab"
120 
121  CrossFadeImage {
122  id: wallpaper
123  anchors.fill: parent
124  sourceSize { height: root.height; width: root.width }
125  fillMode: Image.PreserveAspectCrop
126  }
127 
128  Repeater {
129  id: appRepeater
130  model: ApplicationManager
131  objectName: "appRepeater"
132 
133  delegate: FocusScope {
134  id: appDelegate
135  z: ApplicationManager.count - index
136  y: units.gu(3)
137  width: units.gu(60)
138  height: units.gu(50)
139  focus: model.appId === priv.focusedAppId
140 
141  readonly property int minWidth: units.gu(10)
142  readonly property int minHeight: units.gu(10)
143 
144  property bool maximized: false
145  property bool minimized: false
146 
147  onFocusChanged: {
148  if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
149  ApplicationManager.focusApplication(model.appId);
150  }
151  }
152 
153  Binding {
154  target: ApplicationManager.get(index)
155  property: "requestedState"
156  // TODO: figure out some lifecycle policy, like suspending minimized apps
157  // if running on a tablet or something.
158  // TODO: If the device has a dozen suspended apps because it was running
159  // in staged mode, when it switches to Windowed mode it will suddenly
160  // resume all those apps at once. We might want to avoid that.
161  value: ApplicationInfoInterface.RequestedRunning // Always running for now
162  }
163 
164  function maximize() {
165  minimized = false;
166  maximized = true;
167  }
168  function minimize() {
169  maximized = false;
170  minimized = true;
171  }
172  function unmaximize() {
173  minimized = false;
174  maximized = false;
175  }
176 
177  states: [
178  State {
179  name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized
180  },
181  State {
182  name: "maximized"; when: appDelegate.maximized
183  PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
184  },
185  State {
186  name: "minimized"; when: appDelegate.minimized
187  PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
188  }
189  ]
190  transitions: [
191  Transition {
192  from: "maximized,minimized,normal,"
193  to: "maximized,minimized,normal,"
194  PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
195  },
196  Transition {
197  from: ""
198  to: "altTab"
199  PropertyAction { target: appDelegate; properties: "y,angle,z,itemScale,itemScaleOriginY" }
200  PropertyAction { target: decoratedWindow; properties: "anchors.topMargin" }
201  PropertyAnimation {
202  target: appDelegate; properties: "x"
203  from: root.width
204  duration: rightEdgePushArea.containsMouse ? UbuntuAnimation.FastDuration :0
205  easing: UbuntuAnimation.StandardEasing
206  }
207  }
208  ]
209 
210  Binding {
211  id: previewBinding
212  target: appDelegate
213  property: "z"
214  value: ApplicationManager.count + 1
215  when: index == spread.highlightedIndex && blurLayer.ready
216  }
217 
218  WindowMoveResizeArea {
219  id: windowMoveResizeArea
220  target: appDelegate
221  minWidth: appDelegate.minWidth
222  minHeight: appDelegate.minHeight
223  resizeHandleWidth: units.gu(2)
224  windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
225 
226  onPressed: { ApplicationManager.focusApplication(model.appId) }
227  }
228 
229  DecoratedWindow {
230  id: decoratedWindow
231  objectName: "decoratedWindow"
232  anchors.left: appDelegate.left
233  anchors.top: appDelegate.top
234  width: appDelegate.width
235  height: appDelegate.height
236  application: ApplicationManager.get(index)
237  active: ApplicationManager.focusedApplicationId === model.appId
238  focus: true
239 
240  onClose: ApplicationManager.stopApplication(model.appId)
241  onMaximize: appDelegate.maximize()
242  onMinimize: appDelegate.minimize()
243  }
244  }
245  }
246  }
247 
248  BlurLayer {
249  id: blurLayer
250  anchors.fill: parent
251  source: appContainer
252  visible: false
253  }
254 
255  Rectangle {
256  id: spreadBackground
257  anchors.fill: parent
258  color: "#55000000"
259  visible: false
260  }
261 
262  MouseArea {
263  id: eventEater
264  anchors.fill: parent
265  visible: spreadBackground.visible
266  enabled: visible
267  }
268 
269  DesktopSpread {
270  id: spread
271  objectName: "spread"
272  anchors.fill: parent
273  workspace: appContainer
274  focus: state == "altTab"
275  }
276 }