aboutsummaryrefslogtreecommitdiffstats
path: root/src/imports/controls/StackView.qml
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@theqtcompany.com>2015-03-28 21:51:10 +0100
committerJ-P Nurmi <jpnurmi@theqtcompany.com>2015-04-09 21:54:23 +0000
commit1315f9d97170d7f1bb8ef28ab95e0fafc3e72b68 (patch)
treed7d7b58282ecdd07da4b6ef5305e1d091c623c05 /src/imports/controls/StackView.qml
parentb898b8c2317cd2c7349a60eba0c9bfea395e614c (diff)
Implement StackView in C++
Change-Id: Ia5387aa16325453c676a2542f80c827d4c069ca9 Reviewed-by: J-P Nurmi <jpnurmi@theqtcompany.com>
Diffstat (limited to 'src/imports/controls/StackView.qml')
-rw-r--r--src/imports/controls/StackView.qml914
1 files changed, 9 insertions, 905 deletions
diff --git a/src/imports/controls/StackView.qml b/src/imports/controls/StackView.qml
index 2c75f063..396778b7 100644
--- a/src/imports/controls/StackView.qml
+++ b/src/imports/controls/StackView.qml
@@ -34,921 +34,25 @@
**
****************************************************************************/
-import QtQuick 2.2
+import QtQuick 2.4
import QtQuick.Controls 2.0
-import "StackView.js" as JSArray
-
-/*!
- \qmltype StackView
- \inherits Item
- \ingroup views
- \inqmlmodule QtQuick.Controls
- \since 5.1
-
- \brief Provides a stack-based navigation model.
-
- \image stackview.png
-
- StackView implements a stack-based navigation model, which can be used
- with a set of interlinked information pages. Items are pushed onto the stack
- as the user navigates deeper into the material, and popped off again when he
- chooses to go back.
-
- The \l{Qt Quick Controls - Touch Gallery}{touch gallery} example is a good
- starting point to understand how StackView works. The following snippet
- from the example shows how it can be used:
-
- \qml
- StackView {
- id: stack
- initialItem: view
-
- Component {
- id: view
-
- MouseArea {
- Text {
- text: stack.depth
- anchors.centerIn: parent
- }
- onClicked: stack.push(view)
- }
- }
- }
- \endqml
-
- \section1 Using StackView in an Application
- Using the StackView in the application is typically a simple matter of adding
- the StackView as a child of a Window. The stack is usually anchored to the
- edges of the window, except at the top or bottom where it might be anchored
- to a status bar, or some other similar UI component. The stack can then be
- used by invoking its navigation methods. The first item to show in the StackView
- is commonly loaded assigning it to \l initialItem.
-
- \note Items pushed onto the stack view have \l{Supported Attached Properties}{Stack attached properties}.
-
- \section1 Basic Navigation
- There are three primary navigation operations in StackView: push(), pop(), and
- replace (replace by specifying argument \c replace to push()).
- These correspond to classic stack operations where "push" adds an item to the
- top of a stack, "pop" removes the top item from the stack, and "replace" is like a
- pop followed by a push in that it replaces the topmost item on the stack with
- a new item (but the applied transtition might be different). The topmost item
- in the stack corresponds to the one that is \l{StackView::currentItem} {currently}
- visible on the screen. That means that "push" is the logical equivalent of navigating
- forward or deeper into the application, "pop" is the equivalent of navigating back,
- and "replace" is the equivalent of replacing the current item.
-
- Sometimes it is necessary to go back more than a single step in the stack, for
- example, to return to a "main" item or some kind of section item in the application.
- For this use case, it is possible to specify an item as a parameter for pop().
- This is called an "unwind" operation as the stack gets unwound to the specified item.
- If the item is not found then the stack unwinds until there is only a single item in
- the stack, which becomes the current item. To explicitly unwind to the bottom
- of the stack it is recommended to use \l{pop()} {pop(null)}, though technically any
- non-existent item will do.
-
- Given the stack [A, B, C]:
-
- \list
- \li \l{push()}{push(D)} => [A, B, C, D] - "push" transition animation between C and D
- \li pop() => [A, B] - "pop" transition animation between C and B
- \li \l{push()}{push(D, replace)} => [A, B, D] - "replace" transition between C and D
- \li \l{pop()}{pop(A)} => [A] - "pop" transition between C and A
- \endlist
-
- \note Note that when the stack is empty, a push() will not perform a
- transition animation because there is nothing to transition from (typically during
- application start-up). A pop() on a stack with depth 1 or 0 is a no-operation.
- If removing all items from the stack is needed, a separate function clear() is
- available.
-
- Calling push() returns the item that was pushed onto the stack.
- Calling pop() returns the item that was popped off the stack. When pop() is
- called in an unwind operation the top-most item (the first item that was
- popped, which will also be the one transitioning out) is returned.
-
- \section1 Deep Linking
- \e{Deep linking} means launching an application into a particular state. For example,
- a newspaper application could be launched into showing a particular article,
- bypassing the front item (and possibly a section item) that would normally have
- to be navigated through to get to the article concerned. In terms of StackView, deep
- linking means the ability to modify the state of the stack, so much so that it is
- possible to push a set of items to the top of the stack, or to completely reset
- the stack to a given state.
-
- The API for deep linking in StackView is the same as for basic navigation. Pushing
- an array instead of a single item, will involve that all the items in that array will
- be pushed onto the stack. The transition animation, however, will be conducted as
- if only the last item in the array was pushed onto the stack. The normal semantics
- of push() apply for deep linking, meaning that push() adds whatever is pushed onto
- the stack. Note also that only the last item of the array will be loaded.
- The rest will be lazy-loaded as needed when entering the screen upon subsequent
- calls to pop (or when requesting the item by using \a get).
-
- This gives us the following result, given the stack [A, B, C]:
-
- \list
- \li \l{push()}{push([D, E, F])} => [A, B, C, D, E, F] - "push" transition animation between C and F
- \li \l{push()}{push([D, E, F], replace)} => [A, B, D, E, F] - "replace" transition animation between C and F
- \li clear(); \l{push()}{push([D, E, F])} => [D, E, F] - no transition animation (since the stack was empty)
- \endlist
-
- \section1 Pushing items
-
- An item pushed onto the StackView can be either an Item, a URL, a string
- with a URL, or a Component. To push it, assign it to a property "item"
- inside a property list, and send it as an argument to \l{StackView::push}{push}:
-
- \code
- stackView.push({item: yourItem})
- \endcode
-
- The list can contain several properties that control how the item should be pushed:
- \list
- \li \c item: this property is required, and holds the item to be pushed.
- \li \c properties: a list of QML properties to be assigned to the item upon push. These
- properties will be copied into the item at load time, or when the item will become
- the current item (normally upon push).
- \li \c immediate: set this property to \c true to skip transition effects. When pushing
- an array, this property only needs to be set on the first element to make the
- whole operation immediate.
- \li \c replace: set this property to replace the current item on the stack. When pushing
- an array, you only need to set this property on the first element to replace
- as many elements on the stack as inside the array.
- \li \c destroyOnPop: set this boolean to true if StackView needs to destroy the item when
- it is popped off the stack. By default (if \a destroyOnPop is not specified), StackView
- will destroy items pushed as components or URLs. Items not destroyed will be re-parented
- back to the original parents they had before being pushed onto the stack and hidden.
- If you need to set this property, do it with care, so that items are not leaked.
- \endlist
-
- If the only argument needed is "item", the following short-hand notation can be applied:
-
- \code
- stackView.push(yourItem)
- \endcode
-
- You can push several items in one go by using an array of property lists. This is
- optimizing compared to pushing items one by one, since StackView then can load only the
- last item in the list. The rest will be loaded as they are about to become
- the current item (which happens when the stack is popped). The following example shows how
- to push an array of items:
-
- \code
- stackView.push([{item: yourItem1}, {item: yourItem2}])
- \endcode
-
- If an inline item is pushed, the item is temporarily re-parented into the StackView. When the item
- is later popped off, it gets re-parented back to its original owner again.
- If, however, an item is pushed as a component or a URL, the actual item will be created as an
- item from that component. This happens automatically when the item is about to become the current
- item in the stack. Ownership of the item will then normally be taken by the StackView. It will as
- such automatically destroy the item when it is later popped off. The component that declared the item, by
- contrast, remains in the ownership of the application and is not destroyed by the stack.
- This can be overridden by explicitly setting \c{destroyOnPop} in the list of arguments given to push.
-
- If the \c properties to be pushed are specified, they will be copied into the item at loading time
- (in case of a component or URL), or when the item will become the current item (in case of an inline
- item). The following example shows how this can be done:
-
- \code
- stackView.push({item: someItem, properties: {fgcolor: "red", bgcolor: "blue"}})
- \endcode
-
-
- \note Note that if an item is declared inside another item, and if that parent gets destroyed,
- (even if a component was used), that child item will also be destroyed.
- This follows normal Qt parent-child destruction rules, but sometimes comes as a surprise
- for developers.
-
- \section1 Lifecycle
- An item's lifecycle in the StackView can have the following transitions:
- \list 1
- \li instantiation
- \li inactive
- \li activating
- \li active
- \li deactivating
- \li inactive
- \li destruction
- \endlist
-
- It can move any number of times between inactive and active. When an item is activated,
- it's visible on the screen and is considered to be the current item. An item
- in a StackView that is not visible is not activated, even if the item is currently the
- top-most item in the stack. When the stack becomes visible, the item that is top-most gets
- activated. Likewise if the stack is then hidden, the topmost item would be deactivated.
- Popping the item off the top of the stack at this point would not result in further
- deactivation since the item is not active.
-
- There is an attached \l{Stack::status}{Stack.status} property that tracks the lifecycle. The
- status values list is an enumeration with values \c Stack.Inactive, \c Stack.Activating,
- \c Stack.Active and \c Stack.Deactivating. Combined with the normal \c Component.onComplete and
- \c Component.onDestruction signals the entire lifecycle is thus:
-
- \list
- \li Created: Component.onCompleted()
- \li Activating: Stack.onStatusChanged (Stack.status is Stack.Activating)
- \li Acivated: Stack.onStatusChanged (Stack.status is Stack.Active)
- \li Deactivating: Stack.onStatusChanged (Stack.status is Stack.Deactivating)
- \li Deactivated: Stack.onStatusChanged (Stack.status is Stack.Inactive)
- \li Destruction: Component.onDestruction()
- \endlist
-
- \section1 Finding items
- Sometimes it is necessary to search for an item, for example, in order to unwind the stack to
- an item to which the application does not have a reference. This is facilitated using a
- function find() in StackView. The find() function takes a callback function as its
- only argument. The callback gets invoked for each item in the stack (starting at the top).
- If the callback returns true, then it signals that a match has been found and the find()
- function returns that item. If the callback fails to return true (no match is found),
- then find() returns \c null.
-
- The code below searches for an item in the stack that has a name "order_id" and then unwinds to
- that item. Note that since find() returns \c {null} if no item is found, and since pop unwinds to
- the bottom of the stack if null is given as the target item, the code works well even in
- case no matching item is found.
-
- \code
- stackView.pop(stackView.find(function(item) {
- return item.name == "order_id";
- }));
- \endcode
-
- You can also get to an item in the stack using \l {get()}{get(index)}. You should use
- this function if your item depends on another item in the stack, as the function will
- ensure that the item at the given index gets loaded before it is returned.
-
- \code
- previousItem = stackView.get(myItem.Stack.index - 1));
- \endcode
-
- \section1 Transitions
-
- A transition is performed whenever a item is pushed or popped, and consists of
- two items: enterItem and exitItem. The StackView itself will never move items
- around, but instead delegate the job to an external animation set provided
- by the style or the application developer. How items should visually enter and leave the stack
- (and the geometry they should end up with) is therefore completely controlled from the outside.
-
- When the transition starts, the StackView will search for a transition that
- matches the operation executed. There are three transitions to choose
- from: pushTransition, popTransition, and replaceTransition. Each implements how
- enterItem should animate in, and exitItem out. The transitions are
- collected inside a StackViewDelegate object assigned to
- \l {StackView::delegate}{delegate}. By default, popTransition and
- replaceTransition will be the same as pushTransition, unless you set them
- to something else.
-
- A simple fade transition could be implemented as:
-
- \qml
- StackView {
- delegate: StackViewDelegate {
- function transitionFinished(properties)
- {
- properties.exitItem.opacity = 1
- }
-
- pushTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "opacity"
- from: 0
- to: 1
- }
- PropertyAnimation {
- target: exitItem
- property: "opacity"
- from: 1
- to: 0
- }
- }
- }
- }
- \endqml
-
- PushTransition needs to inherit from StackViewTransition, which is a ParallelAnimation that
- contains the properties \c enterItem and \c exitItem. You set the target of your
- inner animations to those items. Since the same items instance can be pushed several
- times to a StackView, you should always override
- \l {StackViewDelegate::transitionFinished()}{StackViewDelegate.transitionFinished()}.
- Implement this function to reset any properties animated on the exitItem so that later
- transitions can expect the items to be in a default state.
-
- A more complex example could look like the following. Here, the items are lying on the side before
- being rotated to an upright position:
-
- \qml
- StackView {
- delegate: StackViewDelegate {
- function transitionFinished(properties)
- {
- properties.exitItem.x = 0
- properties.exitItem.rotation = 0
- }
-
- pushTransition: StackViewTransition {
- SequentialAnimation {
- ScriptAction {
- script: enterItem.rotation = 90
- }
- PropertyAnimation {
- target: enterItem
- property: "x"
- from: enterItem.width
- to: 0
- }
- PropertyAnimation {
- target: enterItem
- property: "rotation"
- from: 90
- to: 0
- }
- }
- PropertyAnimation {
- target: exitItem
- property: "x"
- from: 0
- to: -exitItem.width
- }
- }
- }
- }
- \endqml
-
- \section2 Advanced usage
-
- When the StackView needs a new transition, it first calls
- \l {StackViewDelegate::getTransition()}{StackViewDelegate.getTransition()}.
- The base implementation of this function just looks for a property named \c properties.name inside
- itself (root), which is how it finds \c {property Component pushTransition} in the examples above.
-
- \code
- function getTransition(properties)
- {
- return root[properties.name]
- }
- \endcode
-
- You can override this function for your delegate if you need extra logic to decide which
- transition to return. You could for example introspect the items, and return different animations
- depending on the their internal state. StackView will expect you to return a Component that
- contains a StackViewTransition, or a StackViewTransition directly. The former is easier, as StackView will
- then create the transition and later destroy it when it's done, while avoiding any sideeffects
- caused by the transition being alive long after it has run. Returning a StackViewTransition directly
- can be useful if you need to write some sort of transition caching for performance reasons.
- As an optimization, you can also return \c null to signal that you just want to show/hide the items
- immediately without creating or running any transitions. You can also override this function if
- you need to alter the items in any way before the transition starts.
-
- \c properties contains the properties that will be assigned to the StackViewTransition before
- it runs. In fact, you can add more properties to this object during the call
- if you need to initialize additional properties of your custom StackViewTransition when the returned
- component is instantiated.
-
- The following example shows how you can decide which animation to use during runtime :
-
- \qml
- StackViewDelegate {
- function getTransition(properties)
- {
- return (properties.enterItem.Stack.index % 2) ? horizontalTransition : verticalTransition
- }
-
- function transitionFinished(properties)
- {
- properties.exitItem.x = 0
- properties.exitItem.y = 0
- }
-
- property Component horizontalTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "x"
- from: target.width
- to: 0
- duration: 300
- }
- PropertyAnimation {
- target: exitItem
- property: "x"
- from: 0
- to: target.width
- duration: 300
- }
- }
-
- property Component verticalTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "y"
- from: target.height
- to: 0
- duration: 300
- }
- PropertyAnimation {
- target: exitItem
- property: "y"
- from: 0
- to: target.height
- duration: 300
- }
- }
- }
- \endqml
-
- \section1 Supported Attached Properties
-
- Items in a StackView support these attached properties:
- \list
- \li \l{Stack::index}{Stack.index} - Contains the index of the item inside the StackView
- \li \l{Stack::view}{Stack.view} - Contains the StackView the item is in
- \li \l{Stack::status}{Stack.status} - Contains the status of the item
- \endlist
-*/
AbstractStackView {
id: root
- depth: root.__depth
- currentItem: root.__currentItem
- initialItem: null
- busy: __currentTransition !== null
-
- /*! The transitions to use when pushing or popping items.
- For better understanding on how to apply custom transitions, read \l{Transitions}.
- \sa {Stack::transitions}{Stack.transitions} */
- property StackViewDelegate delegate: StackViewDelegate {}
-
- /*! Pushes an item onto the stack. The function takes a property list as argument, which
- should contain one or more of the following properties:
- \list
- \li \c item: this property is required, and holds the item you want to push.
- \li \c properties: a list of QML properties that should be assigned
- to the item upon push. These properties will be copied into the item when it is
- loaded (in case of a component or URL), or when it becomes the current item for the
- first time (normally upon push).
- \li \c immediate: set this property to \c true to skip transition effects. When pushing
- an array, you only need to set this property on the first element to make the
- whole operation immediate.
- \li \c replace: set this property to replace the current item on the stack. When pushing
- an array, you only need to set this property on the first element to replace
- as many elements on the stack as inside the array.
- \li \c destroyOnPop: set this property to specify if the item needs to be destroyed
- when its popped off the stack. By default (if \a destroyOnPop is not specified),
- StackView will destroy items pushed as components or URLs. Items
- not destroyed will be re-parented to the original parents they had before being
- pushed onto the stack, and hidden. If you need to set this property, do it with
- care, so that items are not leaked.
- \endlist
-
- You can also push an array of items (property lists) if you need to push several items
- in one go. A transition will then only occur between the current item and the last
- item in the list. Loading the other items will be deferred until needed.
-
- Examples:
- \list
- \li stackView.push({item:anItem})
- \li stackView.push({item:aURL, immediate: true, replace: true})
- \li stackView.push({item:aRectangle, properties:{color:"red"}})
- \li stackView.push({item:aComponent, properties:{color:"red"}})
- \li stackView.push({item:aComponent.createObject(), destroyOnPop:true})
- \li stackView.push([{item:anitem, immediate:true}, {item:aURL}])
- \endlist
-
- \note Note: if the only argument needed is "item", you can apply the following short-
- hand notation: \c{stackView.push(anItem)}.
-
- Returns the item that became current.
-
- \sa initialItem
- \sa {Pushing items}
- */
- function push(item) {
- // Note: we support two different APIs in this function; The old meego API, and
- // the new "property list" API. Hence the reason for hiding the fact that you
- // can pass more arguments than shown in the signature:
- if (__recursionGuard(true))
- return
- var properties = arguments[1]
- var immediate = arguments[2]
- var replace = arguments[3]
- var arrayPushed = (item instanceof Array)
- var firstItem = arrayPushed ? item[0] : item
- immediate = (immediate || JSArray.stackView.length === 0)
-
- if (firstItem && firstItem.item && firstItem.hasOwnProperty("x") === false) {
- // Property list API used:
- immediate = immediate || firstItem.immediate
- replace = replace || firstItem.replace
- }
-
- // Create, and push, a new javascript object, called "element", onto the stack.
- // This element contains all the information necessary to construct the item, and
- // will, after loaded, also contain the loaded item:
- if (arrayPushed) {
- if (item.length === 0)
- return
- var outElement = replace ? JSArray.pop() : JSArray.current()
- for (var i=0; i<item.length; ++i)
- JSArray.push({itemComponent:item[i], loaded: false, index: __depth, properties: properties});
- } else {
- outElement = replace ? JSArray.pop() : JSArray.current()
- JSArray.push({itemComponent:item, loaded: false, index: __depth, properties: properties})
- }
-
- var currentElement = JSArray.current()
- var transition = {
- inElement: currentElement,
- outElement: outElement,
- immediate: immediate,
- replace: replace,
- push: true
- }
- __performTransition(transition)
- __recursionGuard(false)
- return __currentItem
+ popEnter: Transition {
+ NumberAnimation { property: "x"; from: -root.width; to: 0; duration: 400; easing.type: Easing.OutCubic }
}
- /*! Pops one or more items off the stack. The function takes a property list as argument
- which can contain one or more of the following properties:
- \list
- \li \c item: if specified, all items down to (but not including) \a item will be
- popped off. If \a item is \c null, all items down to (but not including) the
- first item will be popped. If not specified, only the current item will be
- popped.
- \li \c immediate: set this property to \c true to skip transition effects.
- \endlist
-
- Examples:
- \list
- \li stackView.pop()
- \li stackView.pop({item:someItem, immediate: true})
- \li stackView.pop({immediate: true})
- \li stackView.pop(null)
- \endlist
-
- \note Note: If the only argument needed is "item", you can apply the following short-
- hand notation: \c{stackView.pop(anItem)}.
-
- Returns the item that was popped off
- \sa clear()
- */
- function pop(item) {
- if (__depth <= 1)
- return null
- if (item && item.hasOwnProperty("x") === false) {
- // Property list API used:
- var immediate = (item.immediate === true)
- item = item.item
- } else {
- immediate = (arguments[1] === true)
- }
-
- if (item === __currentItem)
- return
-
- if (__recursionGuard(true))
- return
-
- var outElement = JSArray.pop()
- var inElement = JSArray.current()
-
- if (__depth > 1 && item !== undefined && item !== inElement.item) {
- // Pop from the top until we find 'item', and return the corresponding
- // element. Skip all non-loaded items (except the first), since no one
- // has any references to such items anyway:
- while (__depth > 1 && !JSArray.current().loaded)
- JSArray.pop()
- inElement = JSArray.current()
- while (__depth > 1 && item !== inElement.item) {
- JSArray.pop()
- __cleanup(inElement)
- while (__depth > 1 && !JSArray.current().loaded)
- JSArray.pop()
- inElement = JSArray.current()
- }
- }
-
- var transition = {
- inElement: inElement,
- outElement: outElement,
- immediate: immediate,
- replace: false,
- push: false
- }
- __performTransition(transition)
- __recursionGuard(false)
- return outElement.item;
- }
-
- /*! Remove all items from the stack. No animations will be applied. */
- function clear() {
- if (__recursionGuard(true))
- return
- if (__currentTransition)
- __currentTransition.animation.complete()
- __currentItem = null
- var count = __depth
- for (var i=0; i<count; ++i) {
- var element = JSArray.pop()
- if (element.item)
- __cleanup(element);
- }
- __recursionGuard(false)
- }
-
- /*! Search for a specific item inside the stack. \a func will
- be called for each item in the stack (with the item as argument)
- until the function returns true. Return value will be the item found. For
- example:
- find(function(item, index) { return item.isTheOne })
- Set \a onlySearchLoadedItems to \c true to not load items that are
- not loaded into memory */
- function find(func, onlySearchLoadedItems) {
- for (var i=__depth-1; i>=0; --i) {
- var element = JSArray.stackView[i];
- if (onlySearchLoadedItems !== true)
- __loadElement(element)
- else if (!element.item)
- continue
- if (func(element.item, i))
- return element.item
- }
- return null;
- }
-
- /*! Returns the item at position \a index in
- the stack. If \a dontLoad is true, the
- item will not be forced to load (and \c null
- will be returned if not yet loaded) */
- function get(index, dontLoad)
- {
- if (index < 0 || index >= JSArray.stackView.length)
- return null
- var element = JSArray.stackView[index]
- if (dontLoad !== true) {
- __loadElement(element)
- return element.item
- } else if (element.item) {
- return element.item
- } else {
- return null
- }
- }
-
- /*! Immediately completes any ongoing transition.
- /sa Animation.complete
- */
- function completeTransition()
- {
- if (__recursionGuard(true))
- return
- if (__currentTransition)
- __currentTransition.animation.complete()
- __recursionGuard(false)
- }
-
- /********* DEPRECATED API *********/
-
- /*! \internal
- \deprecated Use Push() instead */
- function replace(item, properties, immediate) {
- push(item, properties, immediate, true)
+ popExit: Transition {
+ NumberAnimation { property: "x"; from: 0; to: root.width; duration: 400; easing.type: Easing.OutCubic }
}
- /********* PRIVATE API *********/
-
- /*! \internal The currently top-most item on the stack. */
- property Item __currentItem: null
- /*! \internal The number of items currently pushed onto the stack. */
- property int __depth: 0
- /*! \internal Stores the transition info while a transition is ongoing */
- property var __currentTransition: null
- /*! \internal Stops the user from pushing items while preparing a transition */
- property bool __guard: false
-
- Component.onCompleted: {
- if (initialItem)
- push(initialItem)
- }
-
- Component.onDestruction: {
- if (__currentTransition)
- __currentTransition.animation.complete()
- __currentItem = null
- }
-
- /*! \internal */
- function __recursionGuard(use)
- {
- if (use && __guard) {
- console.warn("Warning: StackView: You cannot push/pop recursively!")
- console.trace()
- return true
- }
- __guard = use
- }
-
- /*! \internal */
- function __loadElement(element)
- {
- if (element.loaded) {
- if (!element.item) {
- element.item = invalidItemReplacement.createObject(root)
- element.item.text = "\nError: The item has been deleted outside StackView!"
- }
- return
- }
- if (!element.itemComponent) {
- element.item = invalidItemReplacement.createObject(root)
- element.item.text = "\nError: Invalid item (item was 'null'). "
- + "This might indicate that the item was deleted outside StackView!"
- return
- }
-
- var comp = __resolveComponent(element.itemComponent, element)
-
- // Assign properties to item:
- if (!element.properties)
- element.properties = {}
-
- if (comp.hasOwnProperty("createObject")) {
- if (comp.status === Component.Error) {
- element.item = invalidItemReplacement.createObject(root)
- element.item.text = "\nError: Could not load: " + comp.errorString()
- } else {
- element.item = comp.createObject(root, element.properties)
- // Destroy items we create unless the user specified something else:
- if (!element.hasOwnProperty("destroyOnPop"))
- element.destroyOnPop = true
- }
- } else {
- // comp is already an Item, so just re-parent it into the StackView:
- element.item = comp
- element.originalParent = parent
- element.item.parent = root
- for (var prop in element.properties) {
- if (element.item.hasOwnProperty(prop))
- element.item[prop] = element.properties[prop];
- }
- // Do not destroy items we didn't create, unless the user specified something else:
- if (!element.hasOwnProperty("destroyOnPop"))
- element.destroyOnPop = false
- }
-
-//TODO element.item.Stack.__index = element.index
-//TODO element.item.Stack.__view = root
- // Let item fill all available space by default:
- element.item.width = Qt.binding(function() { return root.width })
- element.item.height = Qt.binding(function() { return root.height })
- element.loaded = true
- }
-
- /*! \internal */
- function __resolveComponent(unknownObjectType, element)
- {
- // We need this extra resolve function since we don't really
- // know what kind of object the user pushed. So we try to
- // figure it out by inspecting the object:
- if (unknownObjectType.hasOwnProperty("createObject")) {
- return unknownObjectType
- } else if (typeof unknownObjectType == "string") {
- return Qt.createComponent(unknownObjectType)
- } else if (unknownObjectType.hasOwnProperty("x")) {
- return unknownObjectType
- } else if (unknownObjectType.hasOwnProperty("item")) {
- // INVARIANT: user pushed a JS-object
- element.properties = unknownObjectType.properties
- if (!unknownObjectType.item)
- unknownObjectType.item = invalidItemReplacement
- if (unknownObjectType.hasOwnProperty("destroyOnPop"))
- element.destroyOnPop = unknownObjectType.destroyOnPop
- return __resolveComponent(unknownObjectType.item, element)
- } else {
- // We cannot determine the type, so assume its a URL:
- return Qt.createComponent(unknownObjectType)
- }
- }
-
- /*! \internal */
- function __cleanup(element) {
- // INVARIANT: element has been removed from JSArray. Destroy its
- // item, or re-parent it back to the parent it had before it was pushed:
- var item = element.item
- if (element.destroyOnPop) {
- item.destroy()
- } else {
- // Mark the item as no longer part of the StackView. It
- // might reenter on pop if pushed several times:
- item.visible = false
- __setStatus(item, Stack.Inactive)
-//TODO item.Stack.__view = null
-//TODO item.Stack.__index = -1
- if (element.originalParent)
- item.parent = element.originalParent
- }
- }
-
- /*! \internal */
- function __setStatus(item, status) {
- item.Stack.status = status
- }
-
- /*! \internal */
- function __performTransition(transition)
- {
- // Animate item in "outElement" out, and item in "inElement" in. Set a guard to protect
- // the user from pushing new items on signals that will fire while preparing for the transition
- // (e.g Stack.onCompleted, Stack.onStatusChanged, Stack.onIndexChanged etc). Otherwise, we will enter
- // this function several times, which causes the items to be updated half-way.
- if (__currentTransition)
- __currentTransition.animation.complete()
- __loadElement(transition.inElement)
-
- transition.name = transition.replace ? "replaceTransition" : (transition.push ? "pushTransition" : "popTransition")
- var enterItem = transition.inElement.item
- transition.enterItem = enterItem
-
- // Since an item can be pushed several times, we need to update its properties:
- enterItem.parent = root
-//TODO enterItem.Stack.__view = root
-//TODO enterItem.Stack.__index = transition.inElement.index
- __currentItem = enterItem
-
- if (!transition.outElement) {
- // A transition consists of two items, but we got just one. So just show the item:
- enterItem.visible = true
- __setStatus(enterItem, Stack.Activating)
- __setStatus(enterItem, Stack.Active)
- return
- }
-
- var exitItem = transition.outElement.item
- transition.exitItem = exitItem
- if (enterItem === exitItem)
- return
-
- if (root.delegate) {
- transition.properties = {
- "name":transition.name,
- "enterItem":transition.enterItem,
- "exitItem":transition.exitItem,
- "immediate":transition.immediate }
- var anim = root.delegate.getTransition(transition.properties)
- if (anim.createObject) {
- anim = anim.createObject(null, transition.properties)
- anim.runningChanged.connect(function(){ if (anim.running === false) anim.destroy() })
- }
- transition.animation = anim
- }
-
- if (!transition.animation) {
- console.warn("Warning: StackView: no", transition.name, "found!")
- return
- }
- if (enterItem.anchors.fill || exitItem.anchors.fill)
- console.warn("Warning: StackView: cannot transition an item that is anchored!")
-
- __currentTransition = transition
- __setStatus(exitItem, Stack.Deactivating)
- enterItem.visible = true
- __setStatus(enterItem, Stack.Activating)
- transition.animation.runningChanged.connect(animationFinished)
- transition.animation.start()
- // NB! For empty animations, "animationFinished" is already
- // executed at this point, leaving __animation === null:
- if (transition.immediate === true && transition.animation)
- transition.animation.complete()
- }
-
- /*! \internal */
- function animationFinished()
- {
- if (!__currentTransition || __currentTransition.animation.running)
- return
-
- __currentTransition.animation.runningChanged.disconnect(animationFinished)
- __currentTransition.exitItem.visible = false
- __setStatus(__currentTransition.exitItem, Stack.Inactive);
- __setStatus(__currentTransition.enterItem, Stack.Active);
- __currentTransition.properties.animation = __currentTransition.animation
- root.delegate.transitionFinished(__currentTransition.properties)
-
- if (!__currentTransition.push || __currentTransition.replace)
- __cleanup(__currentTransition.outElement)
-
- __currentTransition = null
+ pushEnter: Transition {
+ NumberAnimation { property: "x"; from: root.width; to: 0; duration: 400; easing.type: Easing.OutCubic }
}
- /*! \internal */
- property Component invalidItemReplacement: Component {
- Text {
- width: parent.width
- height: parent.height
- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- }
+ pushExit: Transition {
+ NumberAnimation { property: "x"; from: 0; to: -root.width; duration: 400; easing.type: Easing.OutCubic }
}
}