added android components
[mardrone] / mardrone / imports / com / nokia / android.1.1 / PageStack.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Components project.
8 **
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
11 **
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
14 ** met:
15 **   * Redistributions of source code must retain the above copyright
16 **     notice, this list of conditions and the following disclaimer.
17 **   * Redistributions in binary form must reproduce the above copyright
18 **     notice, this list of conditions and the following disclaimer in
19 **     the documentation and/or other materials provided with the
20 **     distribution.
21 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 **     the names of its contributors may be used to endorse or promote
23 **     products derived from this software without specific prior written
24 **     permission.
25 **
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 // The PageStack item defines a container for pages and a stack-based
42 // navigation model. Pages can be defined as QML items or components.
43
44 import QtQuick 1.1
45 import "." 1.1
46
47 import "PageStack.js" as Engine
48
49 Item {
50     id: root
51
52     width: parent ? parent.width : 0
53     height: parent ? parent.height : 0
54
55     property int depth: Engine.getDepth()
56     property Item currentPage: null
57     property ToolBar toolBar
58     property variant initialPage
59
60     // Indicates whether there is an ongoing page transition.
61     property bool busy: internal.ongoingTransitionCount > 0
62
63     // Pushes a page on the stack.
64     // The page can be defined as a component, item or string.
65     // If an item is used then the page will get re-parented.
66     // If a string is used then it is interpreted as a url that is used to load a page component.
67     //
68     // The page can also be given as an array of pages. In this case all those pages will be pushed
69     // onto the stack. The items in the stack can be components, items or strings just like for single
70     // pages. Additionally an object can be used, which specifies a page and an optional properties
71     // property. This can be used to push multiple pages while still giving each of them properties.
72     // When an array is used the transition animation will only be to the last page.
73     //
74     // The properties argument is optional and allows defining a map of properties to set on the page.
75     // If the immediate argument is true then no transition animation is performed.
76     // Returns the page instance.
77     function push(page, properties, immediate) {
78         return Engine.push(page, properties, false, immediate);
79     }
80
81     // Pops a page off the stack.
82     // If page is specified then the stack is unwound to that page, to unwind to the first page specify
83     // page as null. If the immediate argument is true then no transition animation is performed.
84     // Returns the page instance that was popped off the stack.
85     function pop(page, immediate) {
86         return Engine.pop(page, immediate);
87     }
88
89     // Replaces a page on the stack.
90     // See push() for details.
91     function replace(page, properties, immediate) {
92         return Engine.push(page, properties, true, immediate);
93     }
94
95     // Clears the page stack.
96     function clear() {
97         return Engine.clear();
98     }
99
100     // Iterates through all pages (top to bottom) and invokes the specified function.
101     // If the specified function returns true the search stops and the find function
102     // returns the page that the iteration stopped at. If the search doesn't result
103     // in any page being found then null is returned.
104     function find(func) {
105         return Engine.find(func);
106     }
107
108     // Called when the page stack visibility changes.
109     onVisibleChanged: {
110         if (currentPage) {
111             internal.setPageStatus(currentPage, visible ? PageStatus.Active : PageStatus.Inactive);
112             if (visible)
113                 currentPage.visible = currentPage.parent.visible = true;
114         }
115     }
116
117     onInitialPageChanged: {
118         if (initialPage) {
119             if (depth == 0)
120                 push(initialPage, null, true)
121             else if (depth == 1)
122                 replace(initialPage, null, true)
123             else
124                 console.log("Cannot update PageStack.initialPage")
125         }
126     }
127
128     Component.onCompleted: {
129         if (initialPage && depth == 0)
130             push(initialPage, null, true)
131     }
132
133     QtObject {
134         id: internal
135
136         // The number of ongoing transitions.
137         property int ongoingTransitionCount: 0
138
139         // Sets the page status.
140         function setPageStatus(page, status) {
141             if (page != null) {
142                 if (page.status !== undefined) {
143                     if (status == PageStatus.Active && page.status == PageStatus.Inactive)
144                         page.status = PageStatus.Activating;
145                     else if (status == PageStatus.Inactive && page.status == PageStatus.Active)
146                         page.status = PageStatus.Deactivating;
147
148                     page.status = status;
149                 }
150             }
151         }
152     }
153
154     // Component for page containers.
155     Component {
156         id: containerComponent
157
158         Item {
159             id: container
160
161             width: parent ? parent.width : 0
162             height: parent ? parent.height : 0
163
164             // The states correspond to the different possible positions of the container.
165             state: "Hidden"
166
167             // The page held by this container.
168             property Item page: null
169
170             // The owner of the page.
171             property Item owner: null
172
173             // The width of the longer screen dimension
174             property int screenWidth: Math.max(screen.width, screen.height)
175
176             // Duration of transition animation (in ms)
177             property int transitionDuration: 200
178
179             // Flag that indicates the container should be cleaned up after the transition has ended.
180             property bool cleanupAfterTransition: false
181
182             // Flag that indicates if page transition animation is running
183             property bool transitionAnimationRunning: false
184
185             // State to be set after previous state change animation has finished
186             property string pendingState: "none"
187
188             // Ensures that transition finish actions are executed
189             // in case the object is destroyed before reaching the
190             // end state of an ongoing transition
191             Component.onDestruction: {
192                 if (transitionAnimationRunning)
193                     transitionEnded();
194             }
195
196             // Sets pending state as current if state change is delayed
197             onTransitionAnimationRunningChanged: {
198                 if (!transitionAnimationRunning && pendingState != "none") {
199                     state = pendingState;
200                     pendingState = "none";
201                 }
202             }
203
204             // Handles state change depening on transition animation status
205             function setState(newState) {
206                 if (transitionAnimationRunning)
207                     pendingState = newState;
208                 else
209                     state = newState;
210             }
211
212             // Performs a push enter transition.
213             function pushEnter(immediate, orientationChanges) {
214                 if (!immediate) {
215                     if (orientationChanges)
216                         setState("LandscapeRight");
217                     else
218                         setState("Right");
219                 }
220                 setState("");
221                 page.visible = true;
222                 if (root.visible && immediate)
223                     internal.setPageStatus(page, PageStatus.Active);
224             }
225
226             // Performs a push exit transition.
227             function pushExit(replace, immediate, orientationChanges) {
228                 if (orientationChanges)
229                     setState(immediate ? "Hidden" : "LandscapeLeft");
230                 else
231                     setState(immediate ? "Hidden" : "Left");
232                 if (root.visible && immediate)
233                     internal.setPageStatus(page, PageStatus.Inactive);
234                 if (replace) {
235                     if (immediate)
236                         cleanup();
237                     else
238                         cleanupAfterTransition = true;
239                 }
240             }
241
242             // Performs a pop enter transition.
243             function popEnter(immediate, orientationChanges) {
244                 if (!immediate)
245                     state = orientationChanges ? "LandscapeLeft" : "Left";
246                 setState("");
247                 page.visible = true;
248                 if (root.visible && immediate)
249                     internal.setPageStatus(page, PageStatus.Active);
250             }
251
252             // Performs a pop exit transition.
253             function popExit(immediate, orientationChanges) {
254                 if (orientationChanges)
255                     setState(immediate ? "Hidden" : "LandscapeRight");
256                 else
257                     setState(immediate ? "Hidden" : "Right");
258
259                 if (root.visible && immediate)
260                     internal.setPageStatus(page, PageStatus.Inactive);
261                 if (immediate)
262                     cleanup();
263                 else
264                     cleanupAfterTransition = true;
265             }
266
267             // Called when a transition has started.
268             function transitionStarted() {
269                 transitionAnimationRunning = true;
270                 internal.ongoingTransitionCount++;
271                 if (root.visible)
272                     internal.setPageStatus(page, (state == "") ? PageStatus.Activating : PageStatus.Deactivating);
273             }
274
275             // Called when a transition has ended.
276             function transitionEnded() {
277                 if (state != "")
278                     state = "Hidden";
279                 if (root.visible)
280                     internal.setPageStatus(page, (state == "") ? PageStatus.Active : PageStatus.Inactive);
281
282                 internal.ongoingTransitionCount--;
283                 transitionAnimationRunning = false;
284                 if (cleanupAfterTransition)
285                     cleanup();
286             }
287
288             states: [
289                 // Explicit properties for default state.
290                 State {
291                     name: ""
292                     PropertyChanges { target: container; visible: true; opacity: 1 }
293                 },
294                 // Start state for pop entry, end state for push exit.
295                 State {
296                     name: "Left"
297                     PropertyChanges { target: container; x: -width / 2; opacity: 0 }
298                 },
299                 // Start state for pop entry, end state for push exit
300                 // when exiting portrait and entering landscape.
301                 State {
302                     name: "LandscapeLeft"
303                     PropertyChanges { target: container; x: -screenWidth / 2; opacity: 0 }
304                 },
305                 // Start state for push entry, end state for pop exit.
306                 State {
307                     name: "Right"
308                     PropertyChanges { target: container; x: width / 2; opacity: 0 }
309                 },
310                 // Start state for push entry, end state for pop exit
311                 // when exiting portrait and entering landscape.
312                 State {
313                     name: "LandscapeRight"
314                     PropertyChanges { target: container; x: screenWidth / 2; opacity: 0 }
315                 },
316                 // Inactive state.
317                 State {
318                     name: "Hidden"
319                     PropertyChanges { target: container; visible: false }
320                 }
321             ]
322
323             transitions: [
324                 // Push exit transition
325                 Transition {
326                     from: ""; to: "Left"
327                     SequentialAnimation {
328                         ScriptAction { script: transitionStarted() }
329                         ParallelAnimation {
330                             PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
331                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
332                         }
333                         ScriptAction { script: transitionEnded() }
334                     }
335                 },
336                 // Pop entry transition
337                 Transition {
338                     from: "Left"; to: ""
339                     SequentialAnimation {
340                         ScriptAction { script: transitionStarted() }
341                         ParallelAnimation {
342                             PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
343                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
344                         }
345                         ScriptAction { script: transitionEnded() }
346                     }
347                 },
348                 // Push exit transition landscape
349                 Transition {
350                     from: ""; to: "LandscapeLeft"
351                     SequentialAnimation {
352                         ScriptAction { script: transitionStarted() }
353                         ParallelAnimation {
354                             PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
355                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
356                         }
357                         ScriptAction { script: transitionEnded() }
358                     }
359                 },
360                 // Pop entry transition landscape
361                 Transition {
362                     from: "LandscapeLeft"; to: ""
363                     SequentialAnimation {
364                         ScriptAction { script: transitionStarted() }
365                         ParallelAnimation {
366                             PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
367                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
368                         }
369                         ScriptAction { script: transitionEnded() }
370                     }
371                 },
372                 // Pop exit transition
373                 Transition {
374                     from: ""; to: "Right"
375                     SequentialAnimation {
376                         ScriptAction { script: transitionStarted() }
377                         ParallelAnimation {
378                             PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
379                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
380                         }
381                         // Workaround for transition animation bug causing ghost view with page pop transition animation
382                         // TODO: Root cause still unknown
383                         PropertyAnimation {}
384                         ScriptAction { script: transitionEnded() }
385                     }
386                 },
387                 // Push entry transition
388                 Transition {
389                     from: "Right"; to: ""
390                     SequentialAnimation {
391                         ScriptAction { script: transitionStarted() }
392                         ParallelAnimation {
393                             PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
394                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
395                         }
396                         ScriptAction { script: transitionEnded() }
397                     }
398                 },
399                 // Pop exit transition landscape
400                 Transition {
401                     from: ""; to: "LandscapeRight"
402                     SequentialAnimation {
403                         ScriptAction { script: transitionStarted() }
404                         ParallelAnimation {
405                             PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
406                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
407                         }
408                         // Workaround for transition animation bug causing ghost view with page pop transition animation
409                         // TODO: Root cause still unknown
410                         PropertyAnimation {}
411                         ScriptAction { script: transitionEnded() }
412                     }
413                 },
414                 // Push entry transition landscape
415                 Transition {
416                     from: "LandscapeRight"; to: ""
417                     SequentialAnimation {
418                         ScriptAction { script: transitionStarted() }
419                         ParallelAnimation {
420                             PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
421                             PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
422                         }
423                         ScriptAction { script: transitionEnded() }
424                     }
425                 }
426             ]
427
428             // Cleans up the container and then destroys it.
429             function cleanup() {
430                 if (page != null) {
431                     if (page.status == PageStatus.Active)
432                         internal.setPageStatus(page, PageStatus.Inactive);
433                     if (owner != container) {
434                         // container is not the owner of the page - re-parent back to original owner
435                         page.visible = false;
436                         page.parent = owner;
437                     }
438                 }
439                 container.destroy();
440             }
441         }
442     }
443 }
444