1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the Qt Components project.
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
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
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
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."
39 ****************************************************************************/
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.
46 import "PageStack.js" as Engine
51 width: parent ? parent.width : 0
52 height: parent ? parent.height : 0
55 property int depth: Engine.getDepth()
57 // The currently active page.
58 property Item currentPage: null
60 // The application tool bar.
61 property ToolBar toolBar
63 // Indicates whether there is an ongoing page transition.
64 property bool busy: __ongoingTransitionCount > 0
66 // The number of ongoing transitions.
67 property int __ongoingTransitionCount: 0
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.
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.
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);
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);
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);
101 // Clears the page stack.
103 return Engine.clear();
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);
114 // Called when the page stack visibility changes.
117 __setPageStatus(currentPage, visible ? PageStatus.Active : PageStatus.Inactive);
119 currentPage.visible = currentPage.parent.visible = true;
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;
132 page.status = status;
136 // Component for page containers.
138 id: containerComponent
143 width: parent ? parent.width : 0
144 height: parent ? parent.height : 0
146 // The states correspond to the different possible positions of the container.
149 // The page held by this container.
150 property Item page: null
152 // The owner of the page.
153 property Item owner: null
155 // Duration of transition animation (in ms)
156 property int transitionDuration: 500
158 // Flag that indicates the container should be cleaned up after the transition has ended.
159 property bool cleanupAfterTransition: false
161 // Performs a push enter transition.
162 function pushEnter(replace, immediate) {
164 state = replace ? "front" : "right";
168 if (root.visible && immediate) {
169 __setPageStatus(page, PageStatus.Active);
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);
183 cleanupAfterTransition = true;
188 // Performs a pop enter transition.
189 function popEnter(immediate) {
195 if (root.visible && immediate) {
196 __setPageStatus(page, PageStatus.Active);
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);
209 cleanupAfterTransition = true;
213 // Called when a transition has started.
214 function transitionStarted() {
215 __ongoingTransitionCount++;
217 __setPageStatus(page, (state == "") ? PageStatus.Activating : PageStatus.Deactivating);
221 // Called when a transition has ended.
222 function transitionEnded() {
227 __setPageStatus(page, (state == "") ? PageStatus.Active : PageStatus.Inactive);
229 __ongoingTransitionCount--;
230 if (cleanupAfterTransition) {
236 // Explicit properties for default state.
239 PropertyChanges { target: container; visible: true }
241 // Start state for pop entry, end state for push exit.
244 PropertyChanges { target: container; x: -width }
246 // Start state for push entry, end state for pop exit.
249 PropertyChanges { target: container; x: width }
251 // Start state for replace entry.
254 PropertyChanges { target: container; scale: 1.5; opacity: 0.0 }
256 // End state for replace exit.
259 PropertyChanges { target: container; scale: 0.5; opacity: 0.0 }
264 PropertyChanges { target: container; visible: false }
269 // Pop entry and push exit 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(); } }
278 // Push entry and pop exit 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(); } }
287 // Replace entry 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(); }
296 // Replace exit 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(); }
307 // Cleans up the container and then destroys it.
309 if (page.status == PageStatus.Active) {
310 __setPageStatus(page, PageStatus.Inactive);
312 if (owner != container) {
313 // container is not the owner of the page - re-parent back to original owner
314 page.visible = false;