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.
47 import "PageStack.js" as Engine
52 width: parent ? parent.width : 0
53 height: parent ? parent.height : 0
55 property int depth: Engine.getDepth()
56 property Item currentPage: null
57 property ToolBar toolBar
58 property variant initialPage
60 // Indicates whether there is an ongoing page transition.
61 property bool busy: internal.ongoingTransitionCount > 0
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.
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.
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);
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);
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);
95 // Clears the page stack.
97 return Engine.clear();
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);
108 // Called when the page stack visibility changes.
111 internal.setPageStatus(currentPage, visible ? PageStatus.Active : PageStatus.Inactive);
113 currentPage.visible = currentPage.parent.visible = true;
117 onInitialPageChanged: {
120 push(initialPage, null, true)
122 replace(initialPage, null, true)
124 console.log("Cannot update PageStack.initialPage")
128 Component.onCompleted: {
129 if (initialPage && depth == 0)
130 push(initialPage, null, true)
136 // The number of ongoing transitions.
137 property int ongoingTransitionCount: 0
139 // Sets the page status.
140 function setPageStatus(page, status) {
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;
148 page.status = status;
154 // Component for page containers.
156 id: containerComponent
161 width: parent ? parent.width : 0
162 height: parent ? parent.height : 0
164 // The states correspond to the different possible positions of the container.
167 // The page held by this container.
168 property Item page: null
170 // The owner of the page.
171 property Item owner: null
173 // The width of the longer screen dimension
174 property int screenWidth: Math.max(screen.width, screen.height)
176 // Duration of transition animation (in ms)
177 property int transitionDuration: 200
179 // Flag that indicates the container should be cleaned up after the transition has ended.
180 property bool cleanupAfterTransition: false
182 // Flag that indicates if page transition animation is running
183 property bool transitionAnimationRunning: false
185 // State to be set after previous state change animation has finished
186 property string pendingState: "none"
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)
196 // Sets pending state as current if state change is delayed
197 onTransitionAnimationRunningChanged: {
198 if (!transitionAnimationRunning && pendingState != "none") {
199 state = pendingState;
200 pendingState = "none";
204 // Handles state change depening on transition animation status
205 function setState(newState) {
206 if (transitionAnimationRunning)
207 pendingState = newState;
212 // Performs a push enter transition.
213 function pushEnter(immediate, orientationChanges) {
215 if (orientationChanges)
216 setState("LandscapeRight");
222 if (root.visible && immediate)
223 internal.setPageStatus(page, PageStatus.Active);
226 // Performs a push exit transition.
227 function pushExit(replace, immediate, orientationChanges) {
228 if (orientationChanges)
229 setState(immediate ? "Hidden" : "LandscapeLeft");
231 setState(immediate ? "Hidden" : "Left");
232 if (root.visible && immediate)
233 internal.setPageStatus(page, PageStatus.Inactive);
238 cleanupAfterTransition = true;
242 // Performs a pop enter transition.
243 function popEnter(immediate, orientationChanges) {
245 state = orientationChanges ? "LandscapeLeft" : "Left";
248 if (root.visible && immediate)
249 internal.setPageStatus(page, PageStatus.Active);
252 // Performs a pop exit transition.
253 function popExit(immediate, orientationChanges) {
254 if (orientationChanges)
255 setState(immediate ? "Hidden" : "LandscapeRight");
257 setState(immediate ? "Hidden" : "Right");
259 if (root.visible && immediate)
260 internal.setPageStatus(page, PageStatus.Inactive);
264 cleanupAfterTransition = true;
267 // Called when a transition has started.
268 function transitionStarted() {
269 transitionAnimationRunning = true;
270 internal.ongoingTransitionCount++;
272 internal.setPageStatus(page, (state == "") ? PageStatus.Activating : PageStatus.Deactivating);
275 // Called when a transition has ended.
276 function transitionEnded() {
280 internal.setPageStatus(page, (state == "") ? PageStatus.Active : PageStatus.Inactive);
282 internal.ongoingTransitionCount--;
283 transitionAnimationRunning = false;
284 if (cleanupAfterTransition)
289 // Explicit properties for default state.
292 PropertyChanges { target: container; visible: true; opacity: 1 }
294 // Start state for pop entry, end state for push exit.
297 PropertyChanges { target: container; x: -width / 2; opacity: 0 }
299 // Start state for pop entry, end state for push exit
300 // when exiting portrait and entering landscape.
302 name: "LandscapeLeft"
303 PropertyChanges { target: container; x: -screenWidth / 2; opacity: 0 }
305 // Start state for push entry, end state for pop exit.
308 PropertyChanges { target: container; x: width / 2; opacity: 0 }
310 // Start state for push entry, end state for pop exit
311 // when exiting portrait and entering landscape.
313 name: "LandscapeRight"
314 PropertyChanges { target: container; x: screenWidth / 2; opacity: 0 }
319 PropertyChanges { target: container; visible: false }
324 // Push exit transition
327 SequentialAnimation {
328 ScriptAction { script: transitionStarted() }
330 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
331 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
333 ScriptAction { script: transitionEnded() }
336 // Pop entry transition
339 SequentialAnimation {
340 ScriptAction { script: transitionStarted() }
342 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
343 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
345 ScriptAction { script: transitionEnded() }
348 // Push exit transition landscape
350 from: ""; to: "LandscapeLeft"
351 SequentialAnimation {
352 ScriptAction { script: transitionStarted() }
354 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
355 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
357 ScriptAction { script: transitionEnded() }
360 // Pop entry transition landscape
362 from: "LandscapeLeft"; to: ""
363 SequentialAnimation {
364 ScriptAction { script: transitionStarted() }
366 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
367 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
369 ScriptAction { script: transitionEnded() }
372 // Pop exit transition
374 from: ""; to: "Right"
375 SequentialAnimation {
376 ScriptAction { script: transitionStarted() }
378 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
379 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
381 // Workaround for transition animation bug causing ghost view with page pop transition animation
382 // TODO: Root cause still unknown
384 ScriptAction { script: transitionEnded() }
387 // Push entry transition
389 from: "Right"; to: ""
390 SequentialAnimation {
391 ScriptAction { script: transitionStarted() }
393 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
394 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
396 ScriptAction { script: transitionEnded() }
399 // Pop exit transition landscape
401 from: ""; to: "LandscapeRight"
402 SequentialAnimation {
403 ScriptAction { script: transitionStarted() }
405 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
406 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
408 // Workaround for transition animation bug causing ghost view with page pop transition animation
409 // TODO: Root cause still unknown
411 ScriptAction { script: transitionEnded() }
414 // Push entry transition landscape
416 from: "LandscapeRight"; to: ""
417 SequentialAnimation {
418 ScriptAction { script: transitionStarted() }
420 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
421 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
423 ScriptAction { script: transitionEnded() }
428 // Cleans up the container and then destroys it.
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;