File indexing completed on 2024-04-28 15:27:44

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Carson Black <uhhadd@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #pragma once
0008 
0009 #include "columnview.h"
0010 #include <QCache>
0011 #include <QQmlPropertyMap>
0012 #include <QQuickItem>
0013 #include <QRandomGenerator>
0014 
0015 static std::map<quint32, QVariant> s_knownVariants;
0016 class PageRouter;
0017 
0018 class ParsedRoute : public QObject
0019 {
0020     Q_OBJECT
0021 
0022 public:
0023     QString name;
0024     QVariant data;
0025     QVariantMap properties;
0026     bool cache;
0027     QQuickItem *item = nullptr;
0028     void itemDestroyed()
0029     {
0030         item = nullptr;
0031     }
0032     QQuickItem *setItem(QQuickItem *newItem)
0033     {
0034         auto ret = item;
0035         if (ret != nullptr) {
0036             disconnect(ret, &QObject::destroyed, this, &ParsedRoute::itemDestroyed);
0037         }
0038         item = newItem;
0039         if (newItem != nullptr) {
0040             connect(newItem, &QObject::destroyed, this, &ParsedRoute::itemDestroyed);
0041         }
0042         return ret;
0043     }
0044     explicit ParsedRoute(const QString &name = QString(),
0045                          QVariant data = QVariant(),
0046                          QVariantMap properties = QVariantMap(),
0047                          bool cache = false,
0048                          QQuickItem *item = nullptr)
0049         : name(name)
0050         , data(data)
0051         , properties(properties)
0052         , cache(cache)
0053     {
0054         setItem(item);
0055     }
0056     ~ParsedRoute() override
0057     {
0058         if (item) {
0059             item->deleteLater();
0060         }
0061     }
0062     quint32 hash()
0063     {
0064         for (auto i = s_knownVariants.begin(); i != s_knownVariants.end(); i++) {
0065             if (i->second == data) {
0066                 return i->first;
0067             }
0068         }
0069         auto number = QRandomGenerator::system()->generate();
0070         while (s_knownVariants.count(number) > 0) {
0071             number = QRandomGenerator::system()->generate();
0072         }
0073         s_knownVariants[number] = data;
0074         return number;
0075     }
0076     bool equals(const ParsedRoute *rhs, bool countItem = false)
0077     {
0078         /* clang-format off */
0079         return name == rhs->name
0080             && data == rhs->data
0081             && (!countItem || item == rhs->item)
0082             && cache == rhs->cache;
0083         /* clang-format on */
0084     }
0085 };
0086 
0087 struct LRU {
0088     int size = 10;
0089     QList<QPair<QString, quint32>> evictionList;
0090     QMap<QPair<QString, quint32>, int> costs;
0091     QMap<QPair<QString, quint32>, ParsedRoute *> items;
0092 
0093     ParsedRoute *take(QPair<QString, quint32> key)
0094     {
0095         auto ret = items.take(key);
0096         evictionList.removeAll(key);
0097         return ret;
0098     }
0099     int totalCosts()
0100     {
0101         int ret = 0;
0102         for (auto cost : std::as_const(costs)) {
0103             ret += cost;
0104         }
0105         return ret;
0106     }
0107     void setSize(int size = 10)
0108     {
0109         this->size = size;
0110         prune();
0111     }
0112     void prune()
0113     {
0114         while (size < totalCosts()) {
0115             auto key = evictionList.last();
0116             auto item = items.take(key);
0117             delete item;
0118             costs.take(key);
0119             evictionList.takeLast();
0120         }
0121     }
0122     void insert(QPair<QString, quint32> key, ParsedRoute *newItem, int cost)
0123     {
0124         if (items.contains(key)) {
0125             auto item = items.take(key);
0126             evictionList.removeAll(key);
0127             if (item != newItem) {
0128                 delete item;
0129             }
0130         }
0131         costs[key] = cost;
0132         items[key] = newItem;
0133         evictionList.prepend(key);
0134         prune();
0135     }
0136 };
0137 
0138 class PageRouterAttached;
0139 
0140 /**
0141  * Item holding data about when to preload a route.
0142  *
0143  * @code{.qml}
0144  *  [...]
0145  * preload {
0146  *   route: "updates-page"
0147  *   when: updatesCount > 0
0148  * }
0149  * @endcode
0150  */
0151 class PreloadRouteGroup : public QObject
0152 {
0153     Q_OBJECT
0154 
0155     /**
0156      * @brief This property holds the route to preload.
0157      */
0158     Q_PROPERTY(QJSValue route MEMBER m_route WRITE setRoute NOTIFY changed)
0159     QJSValue m_route;
0160 
0161     /**
0162      * @brief This property sets whether the route should be preloaded.
0163      */
0164     Q_PROPERTY(bool when MEMBER m_when NOTIFY changed)
0165     bool m_when;
0166 
0167     void handleChange();
0168     PageRouterAttached *m_parent;
0169 
0170 public:
0171     ~PreloadRouteGroup() override;
0172     void setRoute(QJSValue route)
0173     {
0174         m_route = route;
0175         Q_EMIT changed();
0176     }
0177     PreloadRouteGroup(QObject *parent)
0178         : QObject(parent)
0179     {
0180         m_parent = qobject_cast<PageRouterAttached *>(parent);
0181         Q_ASSERT(m_parent);
0182         connect(this, &PreloadRouteGroup::changed, this, &PreloadRouteGroup::handleChange);
0183     }
0184     Q_SIGNAL void changed();
0185 };
0186 
0187 /**
0188  * Item representing a route the PageRouter can navigate to.
0189  *
0190  * @include pagerouter/PageRoute.qml
0191  *
0192  * @see kirigami::PageRouter
0193  */
0194 class PageRoute : public QObject
0195 {
0196     Q_OBJECT
0197 
0198     /**
0199      * @brief This property holds the name of this route.
0200      *
0201      * This name should be unique per PageRoute in a PageRouter.
0202      * When two PageRoutes have the same name, the one listed first
0203      * in the PageRouter will be used.
0204      */
0205     Q_PROPERTY(QString name MEMBER m_name READ name)
0206 
0207     /**
0208      * @brief This property holds the page component of this route.
0209      *
0210      * This should be an instance of Component with a Kirigami::Page inside
0211      * of it.
0212      */
0213     Q_PROPERTY(QQmlComponent *component MEMBER m_component READ component)
0214 
0215     /**
0216      * @brief This property sets whether pages generated by this route should be cached.
0217      *
0218      * This should be an instance of Component with a Kirigami::Page inside
0219      * of it.
0220      *
0221      * This will not work:
0222      *
0223      * @include pagerouter/PageRouterCachePagesDont.qml
0224      *
0225      * This will work:
0226      *
0227      * @include pagerouter/PageRouterCachePagesDo.qml
0228      *
0229      */
0230     Q_PROPERTY(bool cache MEMBER m_cache READ cache)
0231 
0232     /**
0233      * @brief This property specifies how expensive this route is on memory.
0234      *
0235      * This affects caching, as the sum of costs of routes in the cache
0236      * can never exceed the cache's cap.
0237      */
0238     Q_PROPERTY(int cost MEMBER m_cost)
0239 
0240     Q_CLASSINFO("DefaultProperty", "component")
0241 
0242 Q_SIGNALS:
0243     void preloadDataChanged();
0244     void preloadChanged();
0245 
0246 private:
0247     QString m_name;
0248     QQmlComponent *m_component = nullptr;
0249     bool m_cache = false;
0250     int m_cost = 1;
0251 
0252 public:
0253     QQmlComponent *component()
0254     {
0255         return m_component;
0256     };
0257     QString name()
0258     {
0259         return m_name;
0260     };
0261     bool cache()
0262     {
0263         return m_cache;
0264     };
0265     int cost()
0266     {
0267         return m_cost;
0268     };
0269 };
0270 
0271 /**
0272  * An item managing pages and data of a ColumnView using named routes.
0273  *
0274  * <br> <br>
0275  *
0276  * ## Using a PageRouter
0277  *
0278  * Applications typically manage their contents via elements called "pages" or "screens."
0279  * In Kirigami, these are called @link kirigami::Page  Pages @endlink and are
0280  * arranged in @link PageRoute routes @endlink using a PageRouter to manage them. The PageRouter
0281  * manages a stack of @link kirigami::Page Pages @endlink created from a pool of potential
0282  * @link PageRoute PageRoutes @endlink.
0283  *
0284  * Unlike most traditional stacks, a PageRouter provides functions for random access to its pages
0285  * with navigateToRoute and routeActive.
0286  *
0287  * When your user interface fits the stack paradigm and is likely to use random access navigation,
0288  * using the PageRouter is appropriate. For simpler navigation, it is more appropriate to avoid
0289  * the overhead of a PageRouter by using a @link kirigami::PageRow PageRow  @endlink
0290  * instead.
0291  *
0292  * <br> <br>
0293  *
0294  * ## Navigation Model
0295  *
0296  * A PageRouter draws from a pool of @link PageRoute PageRoutes @endlink in order to construct
0297  * its stack.
0298  *
0299  * @image html PageRouterModel.svg width=50%
0300  *
0301  * <br> <br>
0302  *
0303  * You can push pages onto this stack...
0304  *
0305  * @image html PageRouterPush.svg width=50%
0306  *
0307  * ...or pop them off...
0308  *
0309  * @image html PageRouterPop.svg width=50%
0310  *
0311  * ...or navigate to an arbitrary collection of pages.
0312  *
0313  * @image html PageRouterNavigate.svg width=50%
0314  *
0315  * <br> <br>
0316  *
0317  * Components are able to query the PageRouter about the currently active routes
0318  * on the stack. This is useful for e.g. a card indicating that the page it takes
0319  * the user to is currently active.
0320  *
0321  * <br> <br>
0322  *
0323  * ## Example
0324  *
0325  * @include pagerouter/PageRouter.qml
0326  *
0327  * @see kirigami::PageRouterAttached
0328  * @see kirigami::PageRoute
0329  */
0330 class PageRouter : public QObject, public QQmlParserStatus
0331 {
0332     Q_OBJECT
0333     Q_INTERFACES(QQmlParserStatus)
0334 
0335     /**
0336      * @brief This property holds a list of named routes a PageRouter can navigate to.
0337      *
0338      * @include pagerouter/PageRouterRoutes.qml
0339      */
0340     Q_PROPERTY(QQmlListProperty<PageRoute> routes READ routes)
0341 
0342     Q_CLASSINFO("DefaultProperty", "routes")
0343 
0344     /**
0345      * @brief This property sets the initial route.
0346      *
0347      * `initialRoute` is the page that the PageRouter will push upon
0348      * creation. Changing it after creation will cause the PageRouter to reset
0349      * its state. Not providing an `initialRoute` will result in undefined
0350      * behavior.
0351      *
0352      * @see kirigami::PageRoute::name
0353      * @include pagerouter/PageRouterInitialRoute.qml
0354      */
0355     Q_PROPERTY(QJSValue initialRoute READ initialRoute WRITE setInitialRoute NOTIFY initialRouteChanged)
0356 
0357     /**
0358      * @brief This property holds the ColumnView being puppeted by the PageRouter.
0359      *
0360      * All PageRouters should be created with a ColumnView, and creating one without
0361      * a ColumnView is undefined behaviour.
0362      *
0363      * @warning You should **not** directly interact with a ColumnView being puppeted
0364      * by a PageRouter. Instead, use a PageRouter's functions to manipulate the
0365      * ColumnView.
0366      *
0367      * @include pagerouter/PageRouterColumnView.qml
0368      */
0369     Q_PROPERTY(ColumnView *pageStack MEMBER m_pageStack NOTIFY pageStackChanged)
0370 
0371     /**
0372      * @brief This property holds how large the cache can be.
0373      *
0374      * The combined costs of cached routes will never exceed the cache capacity.
0375      */
0376     Q_PROPERTY(int cacheCapacity READ cacheCapacity WRITE setCacheCapacity)
0377 
0378     /**
0379      * @brief This property holds how large the preloaded pool can be.
0380      *
0381      * The combined costs of preloaded routes will never exceed the pool capacity.
0382      */
0383     Q_PROPERTY(int preloadedPoolCapacity READ preloadedPoolCapacity WRITE setPreloadedPoolCapacity)
0384 
0385     /**
0386      * Exposes the data of all pages on the stack, preferring pages on the top
0387      * (e.g. most recently pushed) to pages pushed on the bottom (least recently
0388      * pushed).
0389      */
0390     Q_PROPERTY(QQmlPropertyMap *params READ params CONSTANT)
0391 
0392 private:
0393     /**
0394      * The map exposing to QML all the params of the stack. This is a
0395      * QQmlPropertyMap to allow binding to param values. This *does* lack
0396      * the ability to drop items, but the amount of all params in an app
0397      * is overwhelmingly likely to be fixed, not dynamic.
0398      */
0399     QSharedPointer<QQmlPropertyMap> m_paramMap;
0400 
0401     /**
0402      * @brief This method reevaluates the properties of the param map by going through all of the
0403      * routes on the stack to determine the topmost value for every parameter.
0404      *
0405      * Should be called for every time a route is pushed, popped, or modified.
0406      */
0407     void reevaluateParamMapProperties();
0408 
0409     /**
0410      * @brief This property holds a list of routes the PageRouter is aware of.
0411      *
0412      * Generally, this should not be mutated from C++, only read.
0413      */
0414     QList<PageRoute *> m_routes;
0415 
0416     /**
0417      * @brief The PageRouter being puppeted.
0418      *
0419      * m_pageRow is the ColumnView this PageRouter puppets.
0420      */
0421     ColumnView *m_pageStack = nullptr;
0422 
0423     /**
0424      * @brief The route that the PageRouter will load on completion.
0425      *
0426      * m_initialRoute is the raw QJSValue from QML that will be
0427      * parsed into a ParsedRoute struct on construction.
0428      * Generally, this should not be mutated from C++, only read.
0429      */
0430     QJSValue m_initialRoute;
0431 
0432     /**
0433      * @brief The current routes pushed on the PageRow.
0434      *
0435      * Generally, the state of m_pageRow and m_currentRoutes
0436      * should be kept in sync. Undesirable behaviour will result
0437      * from desynchronisation of the two.
0438      */
0439     QList<ParsedRoute *> m_currentRoutes;
0440 
0441     /**
0442      * @brief Cached routes.
0443      *
0444      * An LRU cache of ParsedRoutes with items that were previously on the stack.
0445      */
0446     LRU m_cache;
0447 
0448     /** @brief Preloaded routes.
0449      *
0450      * A LRU cache of ParsedRoutes with items that may be on the stack in the future,
0451      * but were not on the stack before.
0452      */
0453     LRU m_preload;
0454 
0455     /**
0456      * @brief Helper function to push a route.
0457      *
0458      * This function has the shared logic between
0459      * navigateToRoute and pushRoute.
0460      */
0461     void push(ParsedRoute *route);
0462 
0463     /**
0464      * @brief This method returns whether m_routes has a key.
0465      *
0466      * This function abstracts the QJSValue.
0467      */
0468     bool routesContainsKey(const QString &key) const;
0469 
0470     /**
0471      * @brief This method returns the component of the specified key for m_routes.
0472      *
0473      * The return value will be a @c nullptr if @p key does not exist in
0474      * m_routes.
0475      */
0476     QQmlComponent *routesValueForKey(const QString &key) const;
0477 
0478     /**
0479      * @brief This method returns the cache status of a key for m_routes.
0480      *
0481      * The return value will be @c false if @p key does not exist in
0482      * m_routes.
0483      */
0484     bool routesCacheForKey(const QString &key) const;
0485 
0486     /**
0487      * @brief This method return the cost of a key for m_routes.
0488      *
0489      * The return value will be -1 if @p key does not exist in
0490      * m_routes.
0491      */
0492     int routesCostForKey(const QString &key) const;
0493 
0494     void preload(ParsedRoute *route);
0495     void unpreload(ParsedRoute *route);
0496 
0497     void placeInCache(ParsedRoute *route);
0498 
0499     static void appendRoute(QQmlListProperty<PageRoute> *list, PageRoute *);
0500 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0501     static int routeCount(QQmlListProperty<PageRoute> *list);
0502     static PageRoute *route(QQmlListProperty<PageRoute> *list, int);
0503 #else
0504     static qsizetype routeCount(QQmlListProperty<PageRoute> *list);
0505     static PageRoute *route(QQmlListProperty<PageRoute> *list, qsizetype);
0506 #endif
0507     static void clearRoutes(QQmlListProperty<PageRoute> *list);
0508 
0509     QVariant dataFor(QObject *object);
0510     bool isActive(QObject *object);
0511     void pushFromObject(QObject *object, QJSValue route, bool replace = false);
0512 
0513     friend class PageRouterAttached;
0514     friend class PreloadRouteGroup;
0515     friend class ParsedRoute;
0516 
0517 protected:
0518     void classBegin() override;
0519     void componentComplete() override;
0520 
0521 public:
0522     PageRouter(QQuickItem *parent = nullptr);
0523     ~PageRouter() override;
0524 
0525     QQmlListProperty<PageRoute> routes();
0526 
0527     QQmlPropertyMap *params()
0528     {
0529         return m_paramMap.data();
0530     }
0531 
0532     QJSValue initialRoute() const;
0533     void setInitialRoute(QJSValue initialRoute);
0534 
0535     int cacheCapacity() const
0536     {
0537         return m_cache.size;
0538     };
0539     void setCacheCapacity(int size)
0540     {
0541         m_cache.setSize(size);
0542     };
0543 
0544     int preloadedPoolCapacity() const
0545     {
0546         return m_preload.size;
0547     };
0548     void setPreloadedPoolCapacity(int size)
0549     {
0550         m_preload.setSize(size);
0551     };
0552 
0553     /**
0554      * @brief This method makes the PageRouter navigate to the specified route.
0555      *
0556      * Calling `navigateToRoute` causes the PageRouter to replace currently
0557      * active pages with the new route.
0558      *
0559      * @param route The given route for the PageRouter to navigate to.
0560      * A route is an array of variants or a single item. A string item will be interpreted
0561      * as a page without associated data. An object item will be interpreted
0562      * as follows:
0563      * @code{.js}
0564      * {
0565      *     "route": "/home" // The named page of the route.
0566      *     "data": QtObject {} // The data to pass to the page.
0567      * }
0568      * @endcode
0569      * Navigating to a route not defined in a PageRouter's routes is undefined
0570      * behavior.
0571      *
0572      * @code{.qml}
0573      * Button {
0574      *     text: "Login"
0575      *     onClicked: {
0576      *         Kirigami.PageRouter.navigateToRoute(["/home", "/login"])
0577      *     }
0578      * }
0579      * @endcode
0580      */
0581     Q_INVOKABLE void navigateToRoute(QJSValue route);
0582 
0583     /**
0584      * @brief This method Checs whether the current route is on the stack.
0585      *
0586      * `routeActive` will return @c true if the given route
0587      * is on the stack.
0588      *
0589      * @param route The given route to check for.
0590      *
0591      * `routeActive` returns @c true for partial routes like
0592      * the following:
0593      *
0594      * @code{.js}
0595      * Kirigami.PageRouter.navigateToRoute(["/home", "/login", "/google"])
0596      * Kirigami.PageRouter.routeActive(["/home", "/login"]) // returns true
0597      * @endcode
0598      *
0599      * This only works from the root page, e.g. the following will return false:
0600      * @code{.js}
0601      * Kirigami.PageRouter.navigateToRoute(["/home", "/login", "/google"])
0602      * Kirigami.PageRouter.routeActive(["/login", "/google"]) // returns false
0603      * @endcode
0604      */
0605     Q_INVOKABLE bool routeActive(QJSValue route);
0606 
0607     /**
0608      * @brief This method appends a route to the currently navigated route.
0609      *
0610      * Calling `pushRoute` will append the given @p route to the currently navigated
0611      * routes. See navigateToRoute() if you want to replace the items currently on
0612      * the PageRouter.
0613      *
0614      * @param route The given route to push.
0615      *
0616      * @code{.js}
0617      * Kirigami.PageRouter.navigateToRoute(["/home", "/login"])
0618      * // The PageRouter is navigated to /home/login
0619      * Kirigami.PageRouter.pushRoute("/google")
0620      * // The PageRouter is navigated to /home/login/google
0621      * @endcode
0622      */
0623     Q_INVOKABLE void pushRoute(QJSValue route);
0624 
0625     /**
0626      * @brief This method pops the last page on the router.
0627      *
0628      * Calling `popRoute` will result in the last page on the router getting popped.
0629      * You should not call this function when there is only one page on the router.
0630      *
0631      * @code{.js}
0632      * Kirigami.PageRouter.navigateToRoute(["/home", "/login"])
0633      * // The PageRouter is navigated to /home/login
0634      * Kirigami.PageRouter.popRoute()
0635      * // The PageRouter is navigated to /home
0636      * @endcode
0637      */
0638     Q_INVOKABLE void popRoute();
0639 
0640     /**
0641      * @brief This method shifts keyboard focus and view to a given index on the PageRouter's stack.
0642      *
0643      * @param view The view to bring to focus. If this is an integer index, the PageRouter will
0644      * navigate to the given index. If it's a route specifier, the PageRouter will navigate
0645      * to the first route matching it.
0646      *
0647      * Navigating to route by index:
0648      * @code{.js}
0649      * Kirigami.PageRouter.navigateToRoute(["/home", "/browse", "/apps", "/login"])
0650      * Kirigami.PageRouter.bringToView(1)
0651      * @endcode
0652      *
0653      * Navigating to route by name:
0654      * @code{.js}
0655      * Kirigami.PageRouter.navigateToRoute(["/home", "/browse", "/apps", "/login"])
0656      * Kirigami.PageRouter.bringToView("/browse")
0657      * @endcode
0658      *
0659      * Navigating to route by data:
0660      * @code{.js}
0661      * Kirigami.PageRouter.navigateToRoute([{"route": "/page", "data": "red"},
0662      *                                      {"route": "/page", "data": "blue"},
0663      *                                      {"route": "/page", "data": "green"},
0664      *                                      {"route": "/page", "data": "yellow"}])
0665      * Kirigami.PageRouter.bringToView({"route": "/page", "data": "blue"})
0666      * @endcode
0667      */
0668     Q_INVOKABLE void bringToView(QJSValue route);
0669 
0670     /**
0671      * @brief This method returns a QJSValue corresponding to the current pages on the stack.
0672      *
0673      * The returned value is in the same form as the input to navigateToRoute.
0674      */
0675     Q_INVOKABLE QJSValue currentRoutes() const;
0676 
0677     static PageRouterAttached *qmlAttachedProperties(QObject *object);
0678 
0679 Q_SIGNALS:
0680     void routesChanged();
0681     void initialRouteChanged();
0682     void pageStackChanged();
0683     void currentIndexChanged();
0684     void navigationChanged();
0685 };
0686 
0687 /**
0688  * Attached object allowing children of a PageRouter to access its functions
0689  * without requiring the children to have the parent PageRouter's id.
0690  *
0691  * @see kirigami::PageRouter
0692  */
0693 class PageRouterAttached : public QObject
0694 {
0695     Q_OBJECT
0696 
0697     Q_PROPERTY(PageRouter *router READ router WRITE setRouter NOTIFY routerChanged)
0698     /**
0699      * @brief This property holds data for the page this item belongs to.
0700      * @note Accessing this property outside of a PageRouter will result in undefined behaviour.
0701      */
0702     Q_PROPERTY(QVariant data READ data MEMBER m_data NOTIFY dataChanged)
0703 
0704     /**
0705      * @brief This property sets whether the page this item belongs to is the current index of the ColumnView.
0706      * @note Accessing this property outside of a PageRouter will result in undefined behaviour.
0707      */
0708     Q_PROPERTY(bool isCurrent READ isCurrent NOTIFY isCurrentChanged)
0709 
0710     /**
0711      * @brief This property holds which route this PageRouterAttached should watch for.
0712      * @include pagerouter/PageRouterWatchedRoute.qml
0713      */
0714     Q_PROPERTY(QJSValue watchedRoute READ watchedRoute WRITE setWatchedRoute NOTIFY watchedRouteChanged)
0715 
0716     /**
0717      * @brief This property holds route preloading settings.
0718      */
0719     Q_PROPERTY(PreloadRouteGroup *preload READ preload)
0720 
0721     /**
0722      * Ths property sets whether the watchedRoute is currently active.
0723      */
0724     Q_PROPERTY(bool watchedRouteActive READ watchedRouteActive NOTIFY navigationChanged)
0725 
0726 private:
0727     explicit PageRouterAttached(QObject *parent = nullptr);
0728 
0729     QPointer<PageRouter> m_router;
0730     PreloadRouteGroup *m_preload;
0731     QVariant m_data;
0732     QJSValue m_watchedRoute;
0733 
0734     void findParent();
0735 
0736     friend class PageRouter;
0737     friend class PreloadRouteGroup;
0738     friend class ParsedRoute;
0739 
0740 public:
0741     PreloadRouteGroup *preload() const
0742     {
0743         return m_preload;
0744     };
0745     PageRouter *router() const
0746     {
0747         return m_router;
0748     };
0749     void setRouter(PageRouter *router)
0750     {
0751         m_router = router;
0752         Q_EMIT routerChanged();
0753     }
0754     QVariant data() const;
0755     bool isCurrent() const;
0756     /// @see PageRouter::navigateToRoute()
0757     Q_INVOKABLE void navigateToRoute(QJSValue route);
0758     /// @see PageRouter::routeActive()
0759     Q_INVOKABLE bool routeActive(QJSValue route);
0760     /// @see PageRouter::pushRoute()
0761     Q_INVOKABLE void pushRoute(QJSValue route);
0762     /// @see PageRouter::popRoute()
0763     Q_INVOKABLE void popRoute();
0764     // @see PageRouter::bringToView()
0765     Q_INVOKABLE void bringToView(QJSValue route);
0766     /**
0767      * @brief Push a route from this route on the stack.
0768      *
0769      * Replace the routes after the route this is invoked on
0770      * with the provided @p route.
0771      *
0772      * For example, if you invoke this method on the second route
0773      * in the PageRouter's stack, routes after the second
0774      * route will be replaced with the provided routes.
0775      */
0776     Q_INVOKABLE void pushFromHere(QJSValue route);
0777     /**
0778      * @brief Pop routes after this route on the stack.
0779      *
0780      * Pop the routes after the route this is invoked on with
0781      * the provided @p route.
0782      *
0783      * For example, if you invoke this method on the second route
0784      * in the PageRouter's stack, routes after the second route
0785      * will be removed from the stack.
0786      */
0787     Q_INVOKABLE void popFromHere();
0788     /**
0789      * @brief Replaces this route with the given routes on the stack.
0790      *
0791      * Behaves like pushFromHere, except the current route is also
0792      * popped.
0793      */
0794     Q_INVOKABLE void replaceFromHere(QJSValue route);
0795     bool watchedRouteActive();
0796     void setWatchedRoute(QJSValue route);
0797     QJSValue watchedRoute();
0798 
0799 Q_SIGNALS:
0800     void routerChanged();
0801     void dataChanged();
0802     void isCurrentChanged();
0803     void navigationChanged();
0804     void watchedRouteChanged();
0805 };
0806 
0807 QML_DECLARE_TYPEINFO(PageRouter, QML_HAS_ATTACHED_PROPERTIES)