Unity 8
ApplicationWindow.qml
1 /*
2  * Copyright 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 Lesser 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 Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser 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 
21 FocusScope {
22  id: root
23  implicitWidth: sessionContainer.implicitWidth
24  implicitHeight: sessionContainer.implicitHeight
25 
26  // to be read from outside
27  readonly property bool fullscreen: application ? application.fullscreen : false
28  property alias interactive: sessionContainer.interactive
29  property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
30  readonly property string title: sessionContainer.surface && sessionContainer.surface.name !== "" ?
31  sessionContainer.surface.name : d.name
32 
33  // to be set from outside
34  property QtObject application
35  property int surfaceOrientationAngle
36  property alias resizeSurface: sessionContainer.resizeSurface
37  property int requestedWidth: -1
38  property int requestedHeight: -1
39 
40  readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 0
41  readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 0
42  readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 0
43  readonly property int maximumHeight: sessionContainer.surface ? sessionContainer.surface.maximumHeight : 0
44  readonly property int widthIncrement: sessionContainer.surface ? sessionContainer.surface.widthIncrement : 0
45  readonly property int heightIncrement: sessionContainer.surface ? sessionContainer.surface.heightIncrement : 0
46 
47  QtObject {
48  id: d
49 
50  // helpers so that we don't have to check for the existence of an application everywhere
51  // (in order to avoid breaking qml binding due to a javascript exception)
52  readonly property string name: root.application ? root.application.name : ""
53  readonly property url icon: root.application ? root.application.icon : ""
54  readonly property int applicationState: root.application ? root.application.state : -1
55  readonly property string splashTitle: root.application ? root.application.splashTitle : ""
56  readonly property url splashImage: root.application ? root.application.splashImage : ""
57  readonly property bool splashShowHeader: root.application ? root.application.splashShowHeader : true
58  readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
59  readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
60  readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
61  readonly property url defaultScreenshot: (root.application && root.application.defaultScreenshot !== undefined) ? root.application.defaultScreenshot : ""
62 
63  // Whether the Application had a surface before but lost it.
64  property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
65 
66  readonly property bool needToTakeScreenshot:
67  ((sessionContainer.surface && d.surfaceInitialized) || d.hadSurface)
68  && screenshotImage.status === Image.Null
69  && d.applicationState === ApplicationInfoInterface.Stopped
70  onNeedToTakeScreenshotChanged: {
71  if (needToTakeScreenshot) {
72  screenshotImage.take();
73  }
74  }
75 
76  //FIXME - this is a hack to avoid the first few rendered frames as they
77  // might show the UI accommodating due to surface resizes on startup.
78  // Remove this when possible
79  property bool surfaceInitialized: false
80 
81  readonly property bool supportsSurfaceResize:
82  application &&
83  ((application.supportedOrientations & Qt.PortraitOrientation)
84  || (application.supportedOrientations & Qt.InvertedPortraitOrientation))
85  &&
86  ((application.supportedOrientations & Qt.LandscapeOrientation)
87  || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
88 
89  property bool surfaceOldEnoughToBeResized: false
90  }
91 
92  Binding {
93  target: root.application
94  property: "initialSurfaceSize"
95  value: Qt.size(root.requestedWidth, root.requestedHeight)
96  }
97 
98  Timer {
99  id: surfaceInitTimer
100  interval: 100
101  onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} }
102  }
103 
104  Timer {
105  id: surfaceIsOldTimer
106  interval: 1000
107  onTriggered: { if (stateGroup.state === "surface") { d.surfaceOldEnoughToBeResized = true; } }
108  }
109 
110  Image {
111  id: screenshotImage
112  objectName: "screenshotImage"
113  source: d.defaultScreenshot
114  anchors.fill: parent
115  antialiasing: !root.interactive
116 
117  function take() {
118  // Save memory by using a half-resolution (thus quarter size) screenshot.
119  // Do not make this a binding, we can only take the screenshot once!
120  sourceSize.width = root.width / 2;
121  sourceSize.height = root.height / 2;
122 
123  // Format: "image://application/$APP_ID/$CURRENT_TIME_MS"
124  // eg: "image://application/calculator-app/123456"
125  var timeMs = new Date().getTime();
126  source = "image://application/" + root.application.appId + "/" + timeMs;
127  }
128  }
129 
130  Loader {
131  id: splashLoader
132  visible: active
133  active: false
134  anchors.fill: parent
135  sourceComponent: Component {
136  Splash {
137  id: splash
138  title: d.splashTitle ? d.splashTitle : d.name
139  imageSource: d.splashImage
140  icon: d.icon
141  showHeader: d.splashShowHeader
142  backgroundColor: d.splashColor
143  headerColor: d.splashColorHeader
144  footerColor: d.splashColorFooter
145  }
146  }
147  }
148 
149  SessionContainer {
150  id: sessionContainer
151  // A fake application might not even have a session property.
152  session: application && application.session ? application.session : null
153 
154  requestedWidth: root.requestedWidth
155  requestedHeight: root.requestedHeight
156 
157  surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
158 
159  onSurfaceChanged: {
160  if (sessionContainer.surface) {
161  surfaceInitTimer.start();
162  } else {
163  d.surfaceInitialized = false;
164  }
165  }
166 
167  focus: true
168  }
169 
170  // SessionContainer size drives ApplicationWindow size
171  Binding {
172  target: root; property: "width"
173  value: stateGroup.state === "surface" ? sessionContainer.width : root.requestedWidth
174  when: root.requestedWidth >= 0
175  }
176  Binding {
177  target: root; property: "height"
178  value: stateGroup.state === "surface" ? sessionContainer.height : root.requestedHeight
179  when: root.requestedHeight >= 0
180  }
181 
182  // ApplicationWindow size drives SessionContainer size
183  Binding {
184  target: sessionContainer; property: "width"; value: root.width
185  when: root.requestedWidth < 0
186  }
187  Binding {
188  target: sessionContainer; property: "height"; value: root.height
189  when: root.requestedHeight < 0
190  }
191 
192  StateGroup {
193  id: stateGroup
194  objectName: "applicationWindowStateGroup"
195  states: [
196  State {
197  name: "void"
198  when:
199  d.hadSurface && (!sessionContainer.surface || !d.surfaceInitialized)
200  &&
201  screenshotImage.status !== Image.Ready
202  },
203  State {
204  name: "splashScreen"
205  when:
206  !d.hadSurface && (!sessionContainer.surface || !d.surfaceInitialized)
207  &&
208  screenshotImage.status !== Image.Ready
209  },
210  State {
211  name: "surface"
212  when:
213  (sessionContainer.surface && d.surfaceInitialized)
214  &&
215  (d.applicationState !== ApplicationInfoInterface.Stopped
216  || screenshotImage.status !== Image.Ready)
217  },
218  State {
219  name: "screenshot"
220  when:
221  screenshotImage.status === Image.Ready
222  &&
223  (d.applicationState === ApplicationInfoInterface.Stopped
224  || !sessionContainer.surface || !d.surfaceInitialized)
225  }
226  ]
227 
228  transitions: [
229  Transition {
230  from: ""; to: "splashScreen"
231  PropertyAction { target: splashLoader; property: "active"; value: true }
232  PropertyAction { target: sessionContainer.surfaceContainer
233  property: "visible"; value: false }
234  },
235  Transition {
236  from: "splashScreen"; to: "surface"
237  SequentialAnimation {
238  PropertyAction { target: sessionContainer.surfaceContainer
239  property: "opacity"; value: 0.0 }
240  PropertyAction { target: sessionContainer.surfaceContainer
241  property: "visible"; value: true }
242  UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
243  from: 0.0; to: 1.0
244  duration: UbuntuAnimation.BriskDuration }
245  ScriptAction { script: {
246  splashLoader.active = false;
247  surfaceIsOldTimer.start();
248  } }
249  }
250  },
251  Transition {
252  from: "surface"; to: "splashScreen"
253  SequentialAnimation {
254  ScriptAction { script: {
255  surfaceIsOldTimer.stop();
256  d.surfaceOldEnoughToBeResized = false;
257  splashLoader.active = true;
258  sessionContainer.surfaceContainer.visible = true;
259  } }
260  UbuntuNumberAnimation { target: splashLoader; property: "opacity";
261  from: 0.0; to: 1.0
262  duration: UbuntuAnimation.BriskDuration }
263  PropertyAction { target: sessionContainer.surfaceContainer
264  property: "visible"; value: false }
265  }
266  },
267  Transition {
268  from: "surface"; to: "screenshot"
269  SequentialAnimation {
270  ScriptAction { script: {
271  surfaceIsOldTimer.stop();
272  d.surfaceOldEnoughToBeResized = false;
273  screenshotImage.visible = true;
274  } }
275  UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
276  from: 0.0; to: 1.0
277  duration: UbuntuAnimation.BriskDuration }
278  ScriptAction { script: {
279  sessionContainer.surfaceContainer.visible = false;
280  if (sessionContainer.session) { sessionContainer.session.release(); }
281  } }
282  }
283  },
284  Transition {
285  from: "screenshot"; to: "surface"
286  SequentialAnimation {
287  PropertyAction { target: sessionContainer.surfaceContainer
288  property: "visible"; value: true }
289  UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
290  from: 1.0; to: 0.0
291  duration: UbuntuAnimation.BriskDuration }
292  ScriptAction { script: {
293  screenshotImage.visible = false;
294  screenshotImage.source = "";
295  surfaceIsOldTimer.start();
296  } }
297  }
298  },
299  Transition {
300  from: "splashScreen"; to: "screenshot"
301  SequentialAnimation {
302  PropertyAction { target: screenshotImage
303  property: "visible"; value: true }
304  UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
305  from: 0.0; to: 1.0
306  duration: UbuntuAnimation.BriskDuration }
307  PropertyAction { target: splashLoader; property: "active"; value: false }
308  }
309  },
310  Transition {
311  from: "surface"; to: "void"
312  ScriptAction { script: {
313  surfaceIsOldTimer.stop();
314  d.surfaceOldEnoughToBeResized = false;
315  sessionContainer.surfaceContainer.visible = false;
316  if (sessionContainer.session) { sessionContainer.session.release(); }
317  } }
318  },
319  Transition {
320  from: "void"; to: "surface"
321  SequentialAnimation {
322  PropertyAction { target: sessionContainer.surfaceContainer; property: "opacity"; value: 0.0 }
323  PropertyAction { target: sessionContainer.surfaceContainer; property: "visible"; value: true }
324  UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
325  from: 0.0; to: 1.0
326  duration: UbuntuAnimation.BriskDuration }
327  ScriptAction { script: {
328  surfaceIsOldTimer.start();
329  } }
330  }
331  }
332  ]
333  }
334 
335 }