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