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