Warning, /graphics/krita/libs/libqml/plugins/components/PageStack.qml is written in an unsupported language. File is not indexed.
0001 /****************************************************************************
0002 **
0003 ** SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
0004 **
0005 ** SPDX-FileCopyrightText: 2011 Nokia Corporation and /or its subsidiary(-ies).
0006 ** All rights reserved.
0007 ** Contact: Nokia Corporation (qt-info@nokia.com)
0008 **
0009 ** This file is part of the Qt Components project.
0010 **
0011 ** $QT_BEGIN_LICENSE:BSD$
0012 ** SPDX-License-Identifier: BSD-3-Clause
0013 **
0014 ****************************************************************************/
0015
0016 // The PageStack item defines a container for pages and a stack-based
0017 // navigation model. Pages can be defined as QML items or components.
0018
0019 /**Documented API
0020 Inherits:
0021 Item
0022
0023 Imports:
0024 QtQuick 1.1
0025 .
0026 PageStack.js
0027
0028 Description:
0029 The PageStack component provides a stack-based navigation model that you can use in your application. A stack-based navigation model means that a page of content for your application is pushed onto a stack when the user navigates deeper into the application page hierarchy. The user can then go back to the previous page (or several pages back) by popping a page (or several pages) off the top of the stack.
0030
0031 Properties:
0032 * int depth:
0033 The number of pages on the page stack.
0034
0035 * Item currentPage:
0036 The page in the stack that is currently visible.
0037
0038 * ToolBar toolBar:
0039 The toolbar container for the tools associated with each page. If a toolbar is specified, the tools set for the current page is shown to the user.
0040 If toolbar is null, then no tools are shown even if a page does have tools.
0041
0042 * variant initialPage:
0043 The page to be automatically loaded when this PageStack component gets instantiated.
0044
0045 * bool busy:
0046 Indicates whether there is an ongoing page transition.
0047
0048 Methods:
0049 * clear():
0050 Clears the page stack of all pages.
0051
0052 * find(function):
0053 This iterates, top to bottom, through all the pages in the page stack and passes each page to the given function. If the specified function returns true, the iterating stops and this function returns the page that produced the true result. If no matching page is found in the page stack, null is returned.
0054
0055 * pop(page, immediate):
0056 When you use pop() with no arguments, it pops the top page off the stack and returns that page to the caller. The normal pop transition animation is performed. If the page popped off the stack is based on the Item element, the page is re-parented back to its original parent.
0057 If the page didn't have an original parent (ie, was created with push(Qt.createComponent("foo.qml")) the page instance will be deleted.
0058 If you give a page argument, the stack is unwound to the given page. Any Item-based pages popped off the stack are re-parented to their original parent.
0059 If the given page is not found in the stack, the stack is unwound to the first page in the stack. However, if you specifically want to unwind the page stack to the first page in the stack, it is best to be explicit about what you are doing and use pop(null) rather than guessing a page that is not on the stack.
0060 The immediate argument defaults to false which means the normal transition animation is performed when a page is popped. If you do not want the transition animation to be performed pass a value of true for immediate.
0061 Note: A pop() on a stack with that contains 0 or 1 pages is a no-operation.
0062 Returns: The page that was top-most on the stack before the pop operation.
0063
0064 * push(page, properties, immediate):
0065 Pushes the given page onto the page stack. You can use a component, item or string for the page. If the page is based on the Item element, the page is re-parented. If a string is used then it is interpreted as a URL that is used to load a page component. The push operation results in the appropriate transition animation being run. If you are pushing an array of pages, the transition animation is only shown for the last page.
0066 Returns: The new top page on the stack.
0067 The page argument can also be an array of pages. In this case, all the pages in the array are pushed onto the stack. The items in the array can be components, items or strings just like for pushing a single page.
0068 The page argument can also be an object that specifies a page with properties or even an array of pages with properties.
0069 The properties argument is optional and allows you to specify values for properties in the page being pushed.
0070 The immediate argument defaults to false which means the normal transition animation is performed when a page is pushed. If you do not want the transition animation to be performed pass a value of true for immediate.
0071 Note: When the stack is empty, a push() or replace() does not perform a transition animation because there is no page to transition from. The only time this normally happens is when an application is starting up so it is not appropriate to have a transition animation anyway.
0072 See also Page.
0073
0074 * replace(page, properties, immediate):
0075 Replaces the top-most page on the stack with page. As in the push() operation, you can use a component, item or string for the page, or even an array of pages. If the page is based on the Item element, the page is re- parented. As in the pop() operation, the replaced page on the stack is re- parented back to its original parent.
0076 Returns: The new top page on the stack.
0077 See also push().
0078
0079 **/
0080
0081 import QtQuick 2.3
0082 import org.krita.sketch 1.0
0083 import "."
0084 import "PageStack.js" as Engine
0085
0086 Item {
0087 id: root
0088
0089 width: parent ? parent.width : 0
0090 height: parent ? parent.height : 0
0091
0092 property int depth: Engine.getDepth()
0093 property Item currentPage: null
0094 property Item toolBar
0095 property variant initialPage
0096
0097 property int transitionDuration: Constants.AnimationDuration
0098
0099 // Indicates whether there is an ongoing page transition.
0100 property bool busy: internal.ongoingTransitionCount > 0
0101
0102 // Pushes a page on the stack.
0103 // The page can be defined as a component, item or string.
0104 // If an item is used then the page will get re-parented.
0105 // If a string is used then it is interpreted as a url that is used to load a page component.
0106 //
0107 // The page can also be given as an array of pages. In this case all those pages will be pushed
0108 // onto the stack. The items in the stack can be components, items or strings just like for single
0109 // pages. Additionally an object can be used, which specifies a page and an optional properties
0110 // property. This can be used to push multiple pages while still giving each of them properties.
0111 // When an array is used the transition animation will only be to the last page.
0112 //
0113 // The properties argument is optional and allows defining a map of properties to set on the page.
0114 // If the immediate argument is true then no transition animation is performed.
0115 // Returns the page instance.
0116 function push(page, properties, immediate)
0117 {
0118 return Engine.push(page, properties, false, immediate);
0119 }
0120
0121 // Pops a page off the stack.
0122 // If page is specified then the stack is unwound to that page, to unwind to the first page specify
0123 // page as null. If the immediate argument is true then no transition animation is performed.
0124 // Returns the page instance that was popped off the stack.
0125 function pop(page, immediate)
0126 {
0127 return Engine.pop(page, immediate);
0128 }
0129
0130 // Replaces a page on the stack.
0131 // See push() for details.
0132 function replace(page, properties, immediate)
0133 {
0134 return Engine.push(page, properties, true, immediate);
0135 }
0136
0137 // Clears the page stack.
0138 function clear()
0139 {
0140 return Engine.clear();
0141 }
0142
0143 // Iterates through all pages (top to bottom) and invokes the specified function.
0144 // If the specified function returns true the search stops and the find function
0145 // returns the page that the iteration stopped at. If the search doesn't result
0146 // in any page being found then null is returned.
0147 function find(func)
0148 {
0149 return Engine.find(func);
0150 }
0151
0152 // Called when the page stack visibility changes.
0153 onVisibleChanged: {
0154 if (currentPage) {
0155 internal.setPageStatus(currentPage, visible ? pageStatus.active : pageStatus.inactive);
0156 if (visible)
0157 currentPage.visible = currentPage.parent.visible = true;
0158 }
0159 }
0160
0161 onInitialPageChanged: {
0162 if (!internal.completed) {
0163 return
0164 }
0165
0166 if (initialPage) {
0167 if (depth == 0) {
0168 push(initialPage, null, true)
0169 } else if (depth == 1) {
0170 replace(initialPage, null, true)
0171 } else {
0172 console.log("Cannot update PageStack.initialPage")
0173 }
0174 }
0175 }
0176
0177 Component.onCompleted: {
0178 internal.completed = true
0179 if (initialPage && depth == 0)
0180 push(initialPage, null, true)
0181 }
0182
0183 QtObject {
0184 id: internal
0185
0186 // The number of ongoing transitions.
0187 property int ongoingTransitionCount: 0
0188
0189 //FIXME: there should be a way to access to them without storing it in an ugly way
0190 property bool completed: false
0191
0192 // Sets the page status.
0193 function setPageStatus(page, status)
0194 {
0195 if (page != null) {
0196 if (page.status !== undefined) {
0197 if (status == pageStatus.active && page.status == pageStatus.inactive)
0198 page.status = pageStatus.activating;
0199 else if (status == pageStatus.inactive && page.status == pageStatus.active)
0200 page.status = pageStatus.deactivating;
0201
0202 page.status = status;
0203 }
0204 }
0205 }
0206 }
0207
0208 QtObject {
0209 id: pageStatus;
0210
0211 property int active: 0;
0212 property int inactive: 1;
0213 property int activating: 2;
0214 property int deactivating: 3;
0215 }
0216
0217 // Component for page containers.
0218 Component {
0219 id: containerComponent
0220
0221 Item {
0222 id: container
0223
0224 width: parent ? parent.width : 0
0225 height: parent ? parent.height : 0
0226
0227 // The states correspond to the different possible positions of the container.
0228 state: "Hidden"
0229
0230 // The page held by this container.
0231 property Item page: null
0232
0233 // The owner of the page.
0234 property Item owner: null
0235
0236 // The width of the longer stack dimension
0237 property int stackWidth: Math.max(root.width, root.height)
0238
0239 // Duration of transition animation (in ms)
0240
0241
0242 // Flag that indicates the container should be cleaned up after the transition has ended.
0243 property bool cleanupAfterTransition: false
0244
0245 // Flag that indicates if page transition animation is running
0246 property bool transitionAnimationRunning: false
0247
0248 // State to be set after previous state change animation has finished
0249 property string pendingState: "none"
0250
0251 // Ensures that transition finish actions are executed
0252 // in case the object is destroyed before reaching the
0253 // end state of an ongoing transition
0254 Component.onDestruction: {
0255 if (transitionAnimationRunning)
0256 transitionEnded();
0257 }
0258
0259 // Sets pending state as current if state change is delayed
0260 onTransitionAnimationRunningChanged: {
0261 if (!transitionAnimationRunning && pendingState != "none") {
0262 state = pendingState;
0263 pendingState = "none";
0264 }
0265 }
0266
0267 // Handles state change depending on transition animation status
0268 function setState(newState)
0269 {
0270 if (transitionAnimationRunning)
0271 pendingState = newState;
0272 else
0273 state = newState;
0274 }
0275
0276 // Performs a push enter transition.
0277 function pushEnter(immediate, orientationChanges)
0278 {
0279 if (!immediate) {
0280 if (orientationChanges)
0281 setState("LandscapeRight");
0282 else
0283 setState("Right");
0284 }
0285 setState("");
0286 page.visible = true;
0287 if (root.visible && immediate)
0288 internal.setPageStatus(page, pageStatus.active);
0289 }
0290
0291 // Performs a push exit transition.
0292 function pushExit(replace, immediate, orientationChanges)
0293 {
0294 if (orientationChanges)
0295 setState(immediate ? "Hidden" : "LandscapeLeft");
0296 else
0297 setState(immediate ? "Hidden" : "Left");
0298 if (root.visible && immediate)
0299 internal.setPageStatus(page, pageStatus.inactive);
0300 if (replace) {
0301 if (immediate)
0302 cleanup();
0303 else
0304 cleanupAfterTransition = true;
0305 }
0306 }
0307
0308 // Performs a pop enter transition.
0309 function popEnter(immediate, orientationChanges)
0310 {
0311 if (!immediate)
0312 state = orientationChanges ? "LandscapeLeft" : "Left";
0313 setState("");
0314 page.visible = true;
0315 if (root.visible && immediate)
0316 internal.setPageStatus(page, pageStatus.active);
0317 }
0318
0319 // Performs a pop exit transition.
0320 function popExit(immediate, orientationChanges)
0321 {
0322 if (orientationChanges)
0323 setState(immediate ? "Hidden" : "LandscapeRight");
0324 else
0325 setState(immediate ? "Hidden" : "Right");
0326
0327 if (root.visible && immediate)
0328 internal.setPageStatus(page, pageStatus.inactive);
0329 if (immediate)
0330 cleanup();
0331 else
0332 cleanupAfterTransition = true;
0333 }
0334
0335 // Called when a transition has started.
0336 function transitionStarted()
0337 {
0338 transitionAnimationRunning = true;
0339 internal.ongoingTransitionCount++;
0340 if (root.visible) {
0341 internal.setPageStatus(page, (state == "") ? pageStatus.activating : pageStatus.deactivating);
0342 }
0343 }
0344
0345 // Called when a transition has ended.
0346 function transitionEnded()
0347 {
0348 if (state != "")
0349 state = "Hidden";
0350 if (root.visible)
0351 internal.setPageStatus(page, (state == "") ? pageStatus.active : pageStatus.inactive);
0352
0353 internal.ongoingTransitionCount--;
0354 transitionAnimationRunning = false;
0355 if (cleanupAfterTransition)
0356 cleanup();
0357 }
0358
0359 states: [
0360 // Explicit properties for default state.
0361 State {
0362 name: ""
0363 PropertyChanges { target: container; visible: true; opacity: 1 }
0364 },
0365 // Start state for pop entry, end state for push exit.
0366 State {
0367 name: "Left"
0368 PropertyChanges { target: container; x: -width / 2; opacity: 0 }
0369 },
0370 // Start state for pop entry, end state for push exit
0371 // when exiting portrait and entering landscape.
0372 State {
0373 name: "LandscapeLeft"
0374 PropertyChanges { target: container; x: -stackWidth / 2; opacity: 0 }
0375 },
0376 // Start state for push entry, end state for pop exit.
0377 State {
0378 name: "Right"
0379 PropertyChanges { target: container; x: width / 2; opacity: 0 }
0380 },
0381 // Start state for push entry, end state for pop exit
0382 // when exiting portrait and entering landscape.
0383 State {
0384 name: "LandscapeRight"
0385 PropertyChanges { target: container; x: stackWidth / 2; opacity: 0 }
0386 },
0387 // Inactive state.
0388 State {
0389 name: "Hidden"
0390 PropertyChanges { target: container; visible: false }
0391 }
0392 ]
0393
0394 transitions: [
0395 // Push exit transition
0396 Transition {
0397 from: ""; to: "Left"
0398 SequentialAnimation {
0399 ScriptAction { script: transitionStarted() }
0400 ParallelAnimation {
0401 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
0402 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0403 }
0404 ScriptAction { script: transitionEnded() }
0405 }
0406 },
0407 // Pop entry transition
0408 Transition {
0409 from: "Left"; to: ""
0410 SequentialAnimation {
0411 ScriptAction { script: transitionStarted() }
0412 ParallelAnimation {
0413 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
0414 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0415 }
0416 ScriptAction { script: transitionEnded() }
0417 }
0418 },
0419 // Push exit transition landscape
0420 Transition {
0421 from: ""; to: "LandscapeLeft"
0422 SequentialAnimation {
0423 ScriptAction { script: transitionStarted() }
0424 ParallelAnimation {
0425 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
0426 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0427 }
0428 ScriptAction { script: transitionEnded() }
0429 }
0430 },
0431 // Pop entry transition landscape
0432 Transition {
0433 from: "LandscapeLeft"; to: ""
0434 SequentialAnimation {
0435 ScriptAction { script: transitionStarted() }
0436 ParallelAnimation {
0437 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
0438 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0439 }
0440 ScriptAction { script: transitionEnded() }
0441 }
0442 },
0443 // Pop exit transition
0444 Transition {
0445 from: ""; to: "Right"
0446 SequentialAnimation {
0447 ScriptAction { script: transitionStarted() }
0448 ParallelAnimation {
0449 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
0450 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0451 }
0452 // Workaround for transition animation bug causing ghost view with page pop transition animation
0453 // TODO: Root cause still unknown
0454 PropertyAnimation {}
0455 ScriptAction { script: transitionEnded() }
0456 }
0457 },
0458 // Push entry transition
0459 Transition {
0460 from: "Right"; to: ""
0461 SequentialAnimation {
0462 ScriptAction { script: transitionStarted() }
0463 ParallelAnimation {
0464 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
0465 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0466 }
0467 ScriptAction { script: transitionEnded() }
0468 }
0469 },
0470 // Pop exit transition landscape
0471 Transition {
0472 from: ""; to: "LandscapeRight"
0473 SequentialAnimation {
0474 ScriptAction { script: transitionStarted() }
0475 ParallelAnimation {
0476 PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
0477 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0478 }
0479 // Workaround for transition animation bug causing ghost view with page pop transition animation
0480 // TODO: Root cause still unknown
0481 PropertyAnimation {}
0482 ScriptAction { script: transitionEnded() }
0483 }
0484 },
0485 // Push entry transition landscape
0486 Transition {
0487 from: "LandscapeRight"; to: ""
0488 SequentialAnimation {
0489 ScriptAction { script: transitionStarted() }
0490 ParallelAnimation {
0491 PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
0492 PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
0493 }
0494 ScriptAction { script: transitionEnded() }
0495 }
0496 }
0497 ]
0498
0499 // Cleans up the container and then destroys it.
0500 function cleanup()
0501 {
0502 if (page != null) {
0503 if (page.status == pageStatus.active) {
0504 internal.setPageStatus(page, pageStatus.inactive)
0505 }
0506 if (owner != container) {
0507 // container is not the owner of the page - re-parent back to original owner
0508 page.visible = false;
0509 page.anchors.fill = undefined
0510 page.parent = owner;
0511 }
0512 }
0513
0514 container.destroy();
0515 }
0516 }
0517 }
0518 }
0519