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