improved desktop UI
[mardrone] / mardrone / imports / com / nokia / meego / 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.0
46 import "PageStack.js" as Engine
47
48 Item {
49     id: root
50
51     width: parent ? parent.width : 0
52     height: parent ? parent.height : 0
53
54     // Page stack depth.
55     property int depth: Engine.getDepth()
56
57     // The currently active page.
58     property Item currentPage: null
59
60     // The application tool bar.
61     property ToolBar toolBar
62
63     // Indicates whether there is an ongoing page transition.
64     property bool busy: __ongoingTransitionCount > 0
65
66     // The number of ongoing transitions.
67     property int __ongoingTransitionCount: 0
68
69     // Pushes a page on the stack.
70     // The page can be defined as a component, item or string.
71     // If an item is used then the page will get re-parented.
72     // If a string is used then it is interpreted as a url that is used to load a page component.
73     //
74     // The page can also be given as an array of pages. In this case all those pages will be pushed
75     // onto the stack. The items in the stack can be components, items or strings just like for single
76     // pages. Additionally an object can be used, which specifies a page and an optional properties
77     // property. This can be used to push multiple pages while still giving each of them properties.
78     // When an array is used the transition animation will only be to the last page.
79     //
80     // The properties argument is optional and allows defining a map of properties to set on the page.
81     // If the immediate argument is true then no transition animation is performed.
82     // Returns the page instance.
83     function push(page, properties, immediate) {
84         return Engine.push(page, properties, false, immediate);
85     }
86
87     // Pops a page off the stack.
88     // If page is specified then the stack is unwound to that page; null to unwind the to first page.
89     // If the immediate argument is true then no transition animation is performed.
90     // Returns the page instance that was popped off the stack.
91     function pop(page, immediate) {
92         return Engine.pop(page, immediate);
93     }
94
95     // Replaces a page on the stack.
96     // See push() for details.
97     function replace(page, properties, immediate) {
98         return Engine.push(page, properties, true, immediate);
99     }
100
101     // Clears the page stack.
102     function clear() {
103         return Engine.clear();
104     }
105
106     // Iterates through all pages (top to bottom) and invokes the specified function.
107     // If the specified function returns true the search stops and the find function
108     // returns the page that the iteration stopped at. If the search doesn't result
109     // in any page being found then null is returned.
110     function find(func) {
111         return Engine.find(func);
112     }
113     
114     // Called when the page stack visibility changes.
115     onVisibleChanged: {
116         if (currentPage) {
117             __setPageStatus(currentPage, visible ? PageStatus.Active : PageStatus.Inactive);
118             if (visible) {
119                 currentPage.visible = currentPage.parent.visible = true;
120             }
121         }
122     }
123
124     // Sets the page status.
125     function __setPageStatus(page, status) {
126         if (page.status !== undefined) {
127             if (status == PageStatus.Active && page.status == PageStatus.Inactive) {
128                 page.status = PageStatus.Activating;
129             } else if (status == PageStatus.Inactive && page.status == PageStatus.Active) {
130                 page.status = PageStatus.Deactivating;
131             }
132             page.status = status;
133         }
134     }
135
136     // Component for page containers.
137     Component {
138         id: containerComponent
139
140         Item {
141             id: container
142
143             width: parent ? parent.width : 0
144             height: parent ? parent.height : 0
145
146             // The states correspond to the different possible positions of the container.
147             state: "hidden"
148
149             // The page held by this container.
150             property Item page: null
151             
152             // The owner of the page.
153             property Item owner: null
154
155             // Duration of transition animation (in ms)
156             property int transitionDuration: 500
157
158             // Flag that indicates the container should be cleaned up after the transition has ended.
159             property bool cleanupAfterTransition: false
160
161             // Performs a push enter transition.
162             function pushEnter(replace, immediate) {
163                 if (!immediate) {
164                     state = replace ? "front" : "right";
165                 }
166                 state = "";
167                 page.visible = true;
168                 if (root.visible && immediate) {
169                     __setPageStatus(page, PageStatus.Active);
170                 }
171             }
172
173             // Performs a push exit transition.
174             function pushExit(replace, immediate) {
175                 state = immediate ? "hidden" : (replace ? "back" : "left");
176                 if (root.visible && immediate) {
177                     __setPageStatus(page, PageStatus.Inactive);
178                 }
179                 if (replace) {
180                     if (immediate) {
181                         cleanup();
182                     } else {
183                         cleanupAfterTransition = true;
184                     }
185                 }
186             }
187
188             // Performs a pop enter transition.
189             function popEnter(immediate) {
190                 if (!immediate) {
191                     state = "left";
192                 }
193                 state = "";
194                 page.visible = true;
195                 if (root.visible && immediate) {
196                     __setPageStatus(page, PageStatus.Active);
197                 }
198             }
199
200             // Performs a pop exit transition.
201             function popExit(immediate) {
202                 state = immediate ? "hidden" : "right";
203                 if (root.visible && immediate) {
204                     __setPageStatus(page, PageStatus.Inactive);
205                 }
206                 if (immediate) {
207                     cleanup();
208                 } else {
209                     cleanupAfterTransition = true;
210                 }
211             }
212             
213             // Called when a transition has started.
214             function transitionStarted() {
215                 __ongoingTransitionCount++;
216                 if (root.visible) {
217                     __setPageStatus(page, (state == "") ? PageStatus.Activating : PageStatus.Deactivating);
218                 }
219             }
220             
221             // Called when a transition has ended.
222             function transitionEnded() {
223                 if (state != "") {
224                     state = "hidden";
225                 }
226                 if (root.visible) {
227                     __setPageStatus(page, (state == "") ? PageStatus.Active : PageStatus.Inactive);
228                 }
229                 __ongoingTransitionCount--;
230                 if (cleanupAfterTransition) {
231                     cleanup();
232                 }
233             }
234
235             states: [
236                 // Explicit properties for default state.
237                 State {
238                     name: ""
239                     PropertyChanges { target: container; visible: true }
240                 },
241                 // Start state for pop entry, end state for push exit.
242                 State {
243                     name: "left"
244                     PropertyChanges { target: container; x: -width }
245                 },
246                 // Start state for push entry, end state for pop exit.
247                 State {
248                     name: "right"
249                     PropertyChanges { target: container; x: width }
250                 },
251                 // Start state for replace entry.
252                 State {
253                     name: "front"
254                     PropertyChanges { target: container; scale: 1.5; opacity: 0.0 }
255                 },
256                 // End state for replace exit.
257                 State {
258                     name: "back"
259                     PropertyChanges { target: container; scale: 0.5; opacity: 0.0 }
260                 },
261                 // Inactive state.
262                 State {
263                     name: "hidden"
264                     PropertyChanges { target: container; visible: false }
265                 }
266             ]
267
268             transitions: [
269                 // Pop entry and push exit transition.
270                 Transition {
271                     from: ""; to: "left"; reversible: true
272                     SequentialAnimation {
273                         ScriptAction { script: if (state == "left") { transitionStarted(); } else { transitionEnded(); } }
274                         PropertyAnimation { properties: "x"; easing.type: Easing.InOutExpo; duration: transitionDuration }
275                         ScriptAction { script: if (state == "left") { transitionEnded(); } else { transitionStarted(); } }
276                     }
277                 },
278                 // Push entry and pop exit transition.
279                 Transition {
280                     from: ""; to: "right"; reversible: true
281                     SequentialAnimation {
282                         ScriptAction { script: if (state == "right") { transitionStarted(); } else { transitionEnded(); } }
283                         PropertyAnimation { properties: "x"; easing.type: Easing.InOutExpo; duration: transitionDuration }
284                         ScriptAction { script: if (state == "right") { transitionEnded(); } else { transitionStarted(); } }
285                     }
286                 },
287                 // Replace entry transition.
288                 Transition {
289                     from: "front"; to: "";
290                     SequentialAnimation {
291                         ScriptAction { script: transitionStarted(); }
292                         PropertyAnimation { properties: "scale,opacity"; easing.type: Easing.InOutExpo; duration: transitionDuration }
293                         ScriptAction { script: transitionEnded(); }
294                     }
295                 },
296                 // Replace exit transition.
297                 Transition {
298                     from: ""; to: "back";
299                     SequentialAnimation {
300                         ScriptAction { script: transitionStarted(); }
301                         PropertyAnimation { properties: "scale,opacity"; easing.type: Easing.InOutExpo; duration: transitionDuration }
302                         ScriptAction { script: transitionEnded(); }
303                    }
304                 }
305             ]
306             
307             // Cleans up the container and then destroys it.
308             function cleanup() {
309                 if (page.status == PageStatus.Active) {
310                     __setPageStatus(page, PageStatus.Inactive);
311                 }
312                 if (owner != container) {
313                     // container is not the owner of the page - re-parent back to original owner
314                     page.visible = false;
315                     page.parent = owner;
316                 }
317                 container.destroy();
318             }
319
320         }
321     }
322
323 }
324