Warning, /frameworks/kirigami/src/controls/PageRow.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Layouts 1.2
0009 import QtQml.Models 2.2
0010 import QtQuick.Templates 2.0 as QT
0011 import QtQuick.Controls 2.0 as QQC2
0012 import org.kde.kirigami 2.20 as Kirigami
0013 import "private/globaltoolbar" as GlobalToolBar
0014 import "templates" as KT
0015 
0016 /**
0017  * PageRow implements a row-based navigation model, which can be used
0018  * with a set of interlinked information pages. Items are pushed in the
0019  * back of the row and the view scrolls until that row is visualized.
0020  * A PageRow can show a single Page or multple pages at the same time,
0021  * depending on the window width: on a phone a single column should be fullscreen,
0022  * while on a tablet or a desktop more than one column can be visible at the same time.
0023  *
0024  * Example usage:
0025  * @include pagerow.qml
0026  *
0027  * @see ColumnViewAttached
0028  * @inherit QtQuick.Templates.Control
0029  */
0030 QT.Control {
0031     id: root
0032 
0033 //BEGIN PROPERTIES
0034     /**
0035      * @brief This property holds the number of items currently pushed onto the view.
0036      * @property int depth
0037      */
0038     property alias depth: columnView.count
0039 
0040     /**
0041      * @brief This property holds the last page in the row.
0042      * @property Page lastItem
0043      */
0044     readonly property Item lastItem: columnView.contentChildren.length > 0 ?  columnView.contentChildren[columnView.contentChildren.length - 1] : null
0045 
0046     /**
0047      * @brief This property points to the currently active page.
0048      * @property Kirigami.Page currentItem
0049      */
0050     property alias currentItem: columnView.currentItem
0051 
0052     /**
0053      * @brief This property holds the index of the currently active page.
0054      * @see ::currentItem
0055      * @property int currentIndex
0056      */
0057     property alias currentIndex: columnView.currentIndex
0058 
0059     /**
0060      * @brief This property sets the initial page/s for this PageRow.
0061      * @note This can optionally be set to an array of pages by using the JavaScript array notation [].
0062      * @property Kirigami.Page initialPage
0063      */
0064     property variant initialPage
0065 
0066     /**
0067      * @brief This property holds the main ColumnView of this Row.
0068      * @see ColumnView
0069      * @property Kirigami.ColumnView contentItem
0070      */
0071     contentItem: columnView
0072 
0073     /**
0074      * @brief This property holds the ColumnView that this PageRow owns.
0075      *
0076      * Generally, you shouldn't need to change the value of this property.
0077      *
0078      * @property Kirigami.ColumnView columnView
0079      * @since org.kde.kirigami 2.12
0080      */
0081     property alias columnView: columnView
0082 
0083     /**
0084      * @brief This property holds a list of all pages in the PageRow.
0085      * @property list<Kirigami.Page> items
0086      * @since org.kde.kirigami 2.6
0087      */
0088     property alias items: columnView.contentChildren;
0089 
0090     /**
0091      * @brief This property holds all visible pages in the PageRow,
0092      * excluding those which are scrolled away.
0093      * @property list<Kirigami.Page> visibleItems
0094      * @since org.kde.kirigami 2.6
0095      */
0096     property alias visibleItems: columnView.visibleItems
0097 
0098     /**
0099      * @brief This property holds the first page in the PageRow that is at least partially visible.
0100      * @note Pages before that one (the one contained in the property) will be out of the viewport.
0101      * @see ColumnView::firstVisibleItem
0102      * @property Item firstVisibleItem
0103      * @since org.kde.kirigami 2.6
0104      */
0105     property alias firstVisibleItem: columnView.firstVisibleItem
0106 
0107     /**
0108      * @brief This property holds the last page in the PageRow that is at least partially visible.
0109      * @note Pages after that one (the one contained in the property) will be out of the viewport.
0110      * @see ColumnView::lastVisibleItem
0111      * @property Item lastVisibleItem
0112      * @since org.kde.kirigami 2.6
0113      */
0114     property alias lastVisibleItem: columnView.lastVisibleItem
0115 
0116     /**
0117      * @brief This property holds the default width for a column.
0118      *
0119      * default: ``20 * Kirigami.Units.gridUnit``
0120      *
0121      * @note Pages can override it using implicitWidth, Layout.fillWidth, Layout.minimumWidth etc.
0122      */
0123     property int defaultColumnWidth: Kirigami.Units.gridUnit * 20
0124 
0125     /**
0126      * @brief This property sets whether it is possible to go back/forward
0127      * by swiping with a gesture on the content view.
0128      *
0129      * default: ``true``
0130      *
0131      * @property bool interactive
0132      */
0133     property alias interactive: columnView.interactive
0134 
0135     /**
0136      * @brief This property specifies whether the PageRow is wide enough to show multiple pages.
0137      * @since KDE Frameworks 5.37
0138      */
0139     readonly property bool wideMode: root.width >= root.defaultColumnWidth*2 && depth >= 2
0140 
0141     /**
0142      * @brief This property sets whether the separators between pages should be displayed.
0143      *
0144      * default: ``true``
0145      *
0146      * @property bool separatorVisible
0147      * @since KDE Frameworks 5.38
0148      */
0149     property alias separatorVisible: columnView.separatorVisible
0150 
0151     /**
0152      * @brief This property sets the appearance of an optional global toolbar for the whole PageRow.
0153      *
0154      * This grouped property has the following sub-properties:
0155      * * ``style: ApplicationHeaderStyle::Status``: how the top bar controls should be represented to the user.
0156      * * ``actualStyle``: this property holds the currently used style. It can be different when ``style`` is set to ``ApplicationHeaderStyle.Auto``
0157      * * ``showNavigationButtons: ApplicationHeaderStyle::NavigationButton``: a
0158      * combination of flags that determines whether to show the back and forward
0159      * button.
0160      * * ``toolbarActionAlignment: Qt::Alignment``: how to horizontally align the actions when using the ``ApplicationHeaderStyle.ToolBar`` style. Note that anything but ``Qt.AlignRight`` will cause the title to be hidden (default: ``Qt.AlignRight``).
0161      * * ``minimumHeight: int`` Minimum height of the header, which will be resized when scrolling. Only in Mobile mode (default: ``preferredHeight``, sliding but no scaling).
0162      * * ``preferredHeight: int`` The height the toolbar will usually have.
0163      * * ``maximumHeight: int `` The height the toolbar will have in mobile mode when the app is in reachable mode (default: preferredHeight * 1.5).
0164      * * ``leftReservedSpace: int, readonly`` How many pixels of extra space are reserved at the left of the page toolbar (typically for navigation buttons or a drawer handle).
0165      * * ``rightReservedSpace: int, readonly`` How many pixels of extra space  are reserved at the right of the page toolbar (typically for a drawer handle).
0166      *
0167      * @property kirigami::private::globaltoolbar::PageRowGlobalToolBarStyleGroup globalToolBar
0168      * @since KDE Frameworks 5.48
0169      */
0170     readonly property alias globalToolBar: globalToolBar
0171 
0172     /**
0173      * @brief This property assigns a drawer as an internal left sidebar for this PageRow.
0174      *
0175      * In this case, when open and not modal, the drawer contents will be in the same layer as the base pagerow.
0176      * Pushing any other layer on top will cover the sidebar.
0177      *
0178      * @since KDE Frameworks 5.84
0179      */
0180     // TODO KF6: globaldrawer should use actions also used by this sidebar instead of reparenting globaldrawer contents?
0181     property OverlayDrawer leftSidebar
0182 
0183     /**
0184      * @brief This property holds the modal layers.
0185      *
0186      * Sometimes an application needs a modal page that always covers all the rows.
0187      * For instance the full screen image of an image viewer or a settings page.
0188      *
0189      * @property QtQuick.Controls.StackView layers
0190      * @since KDE Frameworks 5.38
0191      */
0192     property alias layers: layersStack
0193 
0194     /**
0195      * @brief This property holds whether to automatically pop pages at the top of the stack if they are not visible.
0196      *
0197      * If a user navigates to a previous page on the stack (ex. pressing back button) and pages above
0198      * it on the stack are not visible, they will be popped if this property is true.
0199      *
0200      * @since KDE Frameworks 5.101
0201      */
0202     property bool popHiddenPages: false
0203 //END PROPERTIES
0204 
0205 //BEGIN FUNCTIONS
0206     /**
0207      * @brief Pushes a page on the stack.
0208      *
0209      * The page can be defined as a component, item or string.
0210      * If an item is used then the page will get re-parented.
0211      * If a string is used then it is interpreted as a url that is used to load a page
0212      * component.
0213      * The last pushed page will become the current item.
0214      *
0215      * @param page The page can also be given as an array of pages.
0216      *     In this case all those pages will
0217      *     be pushed onto the stack. The items in the stack can be components, items or
0218      *     strings just like for single pages.
0219      *     Additionally an object can be used, which specifies a page and an optional
0220      *     properties property.
0221      *     This can be used to push multiple pages while still giving each of
0222      *     them properties.
0223      *     When an array is used the transition animation will only be to the last page.
0224      *
0225      * @param properties The properties argument is optional and allows defining a
0226      * map of properties to set on the page. If page is actually an array of pages, properties should also be an array of key/value maps
0227      * @return The new created page (or the last one if it was an array)
0228      */
0229     function push(page, properties) {
0230         const item = insertPage(depth, page, properties);
0231         currentIndex = depth - 1;
0232         return item;
0233     }
0234 
0235     /**
0236      * @brief Pushes a page as a new dialog on desktop and as a layer on mobile.
0237      * @param page The page can be defined as a component, item or string. If an item is
0238      *             used then the page will get re-parented. If a string is used then it
0239      *             is interpreted as a url that is used to load a page component. Once
0240      *             pushed the page gains the methods `closeDialog` allowing to close itself.
0241      *             Kirigami only supports calling `closeDialog` once.
0242      * @param properties The properties given when initializing the page.
0243      * @param windowProperties The properties given to the initialized window on desktop.
0244      * @return Returns a newly created page.
0245      */
0246     function pushDialogLayer(page, properties = {}, windowProperties = {}) {
0247         let item;
0248         if (Kirigami.Settings.isMobile) {
0249             if (QQC2.ApplicationWindow.window.width > Kirigami.Units.gridUnit * 40) {
0250                 // open as a QQC2.Dialog
0251                 const dialog = Qt.createQmlObject(`
0252                     import QtQuick 2.15;
0253                     import QtQuick.Controls 2.15;
0254                     import QtQuick.Layouts 1.15;
0255                     import org.kde.kirigami 2.20 as Kirigami;
0256                     Kirigami.Dialog {
0257                         id: dialog
0258                         modal: true;
0259                         leftPadding: 0; rightPadding: 0; topPadding: 0; bottomPadding: 0;
0260                         clip: true
0261                         header: Kirigami.AbstractApplicationHeader {
0262                             pageRow: null
0263                             page: null
0264                             minimumHeight: Kirigami.Units.gridUnit * 1.6
0265                             maximumHeight: Kirigami.Units.gridUnit * 1.6
0266                             preferredHeight: Kirigami.Units.gridUnit * 1.6
0267 
0268                             Keys.onEscapePressed: event => {
0269                                 if (dialog.opened) {
0270                                     dialog.close();
0271                                 } else {
0272                                     event.accepted = false;
0273                                 }
0274                             }
0275 
0276                             contentItem: RowLayout {
0277                                 width: parent.width
0278                                 Kirigami.Heading {
0279                                     Layout.leftMargin: Kirigami.Units.largeSpacing
0280                                     text: dialog.title
0281                                     elide: Text.ElideRight
0282                                 }
0283                                 Item {
0284                                     Layout.fillWidth: true;
0285                                 }
0286                                 Kirigami.Icon {
0287                                     id: closeIcon
0288                                     Layout.alignment: Qt.AlignVCenter
0289                                     Layout.rightMargin: Kirigami.Units.largeSpacing
0290                                     Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
0291                                     Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
0292                                     source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic"
0293                                     active: closeMouseArea.containsMouse
0294                                     MouseArea {
0295                                         id: closeMouseArea
0296                                         hoverEnabled: true
0297                                         anchors.fill: parent
0298                                         onClicked: mouse => dialog.close();
0299                                     }
0300                                 }
0301                             }
0302                         }
0303                         contentItem: Control { topPadding: 0; leftPadding: 0; rightPadding: 0; bottomPadding: 0; }
0304                     }`, QQC2.ApplicationWindow.overlay);
0305                 dialog.width = Qt.binding(() => QQC2.ApplicationWindow.window.width - Kirigami.Units.gridUnit * 5);
0306                 dialog.height = Qt.binding(() => QQC2.ApplicationWindow.window.height - Kirigami.Units.gridUnit * 5);
0307                 dialog.x = Kirigami.Units.gridUnit * 2.5;
0308                 dialog.y = Kirigami.Units.gridUnit * 2.5;
0309 
0310                 if (typeof page === "string") {
0311                     // url => load component and then load item from component
0312                     const component = Qt.createComponent(Qt.resolvedUrl(page));
0313                     item = component.createObject(dialog.contentItem, properties);
0314                     component.destroy();
0315                     dialog.contentItem.contentItem = item
0316                 } else if (page instanceof Component) {
0317                     item = page.createObject(dialog.contentItem, properties);
0318                     dialog.contentItem.contentItem = item
0319                 } else if (page instanceof Item) {
0320                     item = page;
0321                     page.parent = dialog.contentItem;
0322                 }
0323                 dialog.title = Qt.binding(() => item.title);
0324 
0325                 // Pushing a PageRow is supported but without PageRow toolbar
0326                 if (item.globalToolBar && item.globalToolBar.style) {
0327                     item.globalToolBar.style = Kirigami.ApplicationHeaderStyle.None
0328                 }
0329                 Object.defineProperty(item, 'closeDialog', {
0330                     value: function() {
0331                         dialog.close();
0332                     }
0333                 });
0334                 dialog.open();
0335             } else {
0336                 // open as a layer
0337                 item = layers.push(page, properties);
0338                 Object.defineProperty(item, 'closeDialog', {
0339                     value: function() {
0340                         layers.pop();
0341                     }
0342                 });
0343             }
0344         } else {
0345             // open as a new window
0346             if (!windowProperties.modality) {
0347                 windowProperties.modality = Qt.WindowModal;
0348             }
0349             if (!windowProperties.height) {
0350                 windowProperties.height = Kirigami.Units.gridUnit * 30;
0351             }
0352             if (!windowProperties.width) {
0353                 windowProperties.width = Kirigami.Units.gridUnit * 50;
0354             }
0355             if (!windowProperties.minimumWidth) {
0356                 windowProperties.minimumWidth = Kirigami.Units.gridUnit * 20;
0357             }
0358             if (!windowProperties.minimumHeight) {
0359                 windowProperties.minimumHeight = Kirigami.Units.gridUnit * 15;
0360             }
0361             if (!windowProperties.flags) {
0362                 windowProperties.flags = Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint;
0363             }
0364             const windowComponent = Qt.createComponent(Qt.resolvedUrl("./ApplicationWindow.qml"));
0365             const window = windowComponent.createObject(root, windowProperties);
0366             windowComponent.destroy();
0367             item = window.pageStack.push(page, properties);
0368             Object.defineProperty(item, 'closeDialog', {
0369                 value: function() {
0370                     window.close();
0371                 }
0372             });
0373         }
0374         item.Keys.escapePressed.connect(event => item.closeDialog());
0375         return item;
0376     }
0377 
0378     /**
0379      * @brief Inserts a new page or a list of new pages at an arbitrary position.
0380      *
0381      * The page can be defined as a component, item or string.
0382      * If an item is used then the page will get re-parented.
0383      * If a string is used then it is interpreted as a url that is used to load a page
0384      * component.
0385      * The current Page will not be changed, currentIndex will be adjusted
0386      * accordingly if needed to keep the same current page.
0387      *
0388      * @param page The page can also be given as an array of pages.
0389      *     In this case all those pages will
0390      *     be pushed onto the stack. The items in the stack can be components, items or
0391      *     strings just like for single pages.
0392      *     Additionally an object can be used, which specifies a page and an optional
0393      *     properties property.
0394      *     This can be used to push multiple pages while still giving each of
0395      *     them properties.
0396      *     When an array is used the transition animation will only be to the last page.
0397      *
0398      * @param properties The properties argument is optional and allows defining a
0399      * map of properties to set on the page. If page is actually an array of pages, properties should also be an array of key/value maps
0400      * @return The new created page (or the last one if it was an array)
0401      * @since org.kde.kirigami 2.7
0402      */
0403     function insertPage(position, page, properties) {
0404         if (!page) {
0405             return null
0406         }
0407         //don't push again things already there
0408         if (page.createObject === undefined && typeof page !== "string" && columnView.containsItem(page)) {
0409             print("The item " + page + " is already in the PageRow");
0410             return null;
0411         }
0412 
0413         position = Math.max(0, Math.min(depth, position));
0414 
0415         columnView.pop(columnView.currentItem);
0416 
0417         // figure out if more than one page is being pushed
0418         let pages;
0419         let propsArray = [];
0420         if (page instanceof Array) {
0421             pages = page;
0422             page = pages.pop();
0423             //compatibility with pre-qqc1 api, can probably be removed
0424             if (page.createObject === undefined && page.parent === undefined && typeof page !== "string") {
0425                 properties = properties || page.properties;
0426                 page = page.page;
0427             }
0428         }
0429         if (properties instanceof Array) {
0430             propsArray = properties;
0431             properties = propsArray.pop();
0432         } else {
0433             propsArray = [properties];
0434         }
0435 
0436         // push any extra defined pages onto the stack
0437         if (pages) {
0438             for (let i = 0; i < pages.length; i++) {
0439                 let tPage = pages[i];
0440                 let tProps = propsArray[i];
0441                 //compatibility with pre-qqc1 api, can probably be removed
0442                 if (tPage.createObject === undefined && tPage.parent === undefined && typeof tPage !== "string") {
0443                     if (columnView.containsItem(tPage)) {
0444                         print("The item " + page + " is already in the PageRow");
0445                         continue;
0446                     }
0447                     tProps = tPage.properties;
0448                     tPage = tPage.page;
0449                 }
0450 
0451                 pagesLogic.initAndInsertPage(position, tPage, tProps);
0452                 ++position;
0453             }
0454         }
0455 
0456         // initialize the page
0457         const pageItem = pagesLogic.initAndInsertPage(position, page, properties);
0458 
0459         pagePushed(pageItem);
0460 
0461         return pageItem;
0462     }
0463 
0464     /**
0465      * Move the page at position fromPos to the new position toPos
0466      * If needed, currentIndex will be adjusted
0467      * in order to keep the same current page.
0468      * @since org.kde.kirigami 2.7
0469      */
0470     function movePage(fromPos, toPos) {
0471         columnView.moveItem(fromPos, toPos);
0472     }
0473 
0474     /**
0475      * @brief Remove the given page.
0476      * @param page The page can be given both as integer position or by reference
0477      * @return The page that has just been removed
0478      * @since org.kde.kirigami 2.7
0479      */
0480     function removePage(page) {
0481         if (depth === 0) {
0482             return null;
0483         }
0484 
0485         return columnView.removeItem(page);
0486     }
0487 
0488     /**
0489      * @brief Pops a page off the stack and all the pages after it.
0490      * @param page If page is specified then the stack is unwound to that page,
0491      * to unwind to the first page specify page as null.
0492      * @return The page instance that was popped off the stack.
0493      */
0494     function pop(page) {
0495         if (depth === 0) {
0496             return null;
0497         }
0498 
0499         return columnView.pop(page);
0500     }
0501 
0502     /**
0503      * @brief Replaces a page on the stack.
0504      * @param page The page can also be given as an array of pages.
0505      *     In this case all those pages will
0506      *     be pushed onto the stack. The items in the stack can be components, items or
0507      *     strings just like for single pages.
0508      *     the current page and all pagest after it in the stack will be removed.
0509      *     Additionally an object can be used, which specifies a page and an optional
0510      *     properties property.
0511      *     This can be used to push multiple pages while still giving each of
0512      *     them properties.
0513      *     When an array is used the transition animation will only be to the last page.
0514      * @param properties The properties argument is optional and allows defining a
0515      * map of properties to set on the page.
0516      * @see ::push() for details.
0517      */
0518     function replace(page, properties) {
0519         if (!page) {
0520             return null;
0521         }
0522 
0523         // Remove all pages on top of the one being replaced.
0524         if (currentIndex >= 0) {
0525             columnView.pop(columnView.contentChildren[currentIndex]);
0526         } else {
0527             console.warn("There's no page to replace");
0528         }
0529 
0530         // Figure out if more than one page is being pushed.
0531         let pages;
0532         let propsArray = [];
0533         if (page instanceof Array) {
0534             pages = page;
0535             page = pages.shift();
0536         }
0537         if (properties instanceof Array) {
0538             propsArray = properties;
0539             properties = propsArray.shift();
0540         } else {
0541             propsArray = [properties];
0542         }
0543 
0544         // Replace topmost page.
0545         let pageItem = pagesLogic.initPage(page, properties);
0546         if (depth > 0)
0547             columnView.replaceItem(depth - 1, pageItem);
0548         else {
0549             console.log("Calling replace on empty PageRow", pageItem)
0550             columnView.addItem(pageItem)
0551         }
0552         pagePushed(pageItem);
0553 
0554         // Push any extra defined pages onto the stack.
0555         if (pages) {
0556             for (let i = 0; i < pages.length; i++) {
0557                 const tPage = pages[i];
0558                 const tProps = propsArray[i];
0559 
0560                 pageItem = pagesLogic.initPage(tPage, tProps);
0561                 columnView.addItem(pageItem);
0562                 pagePushed(pageItem);
0563             }
0564         }
0565 
0566         currentIndex = depth - 1;
0567         return pageItem;
0568     }
0569 
0570     /**
0571      * @brief Clears the page stack.
0572      *
0573      * Destroy (or reparent) all the pages contained.
0574      */
0575     function clear() {
0576         return columnView.clear();
0577     }
0578 
0579     /**
0580      * @return the page at idx
0581      * @param idx the depth of the page we want
0582      */
0583     function get(idx) {
0584         return columnView.contentChildren[idx];
0585     }
0586 
0587     /**
0588      * Go back to the previous index and scroll to the left to show one more column.
0589      */
0590     function flickBack() {
0591         if (depth > 1) {
0592             currentIndex = Math.max(0, currentIndex - 1);
0593         }
0594     }
0595 
0596     /**
0597      * Acts as if you had pressed the "back" button on Android or did Alt-Left on desktop,
0598      * "going back" in the layers and page row. Results in a layer being popped if available,
0599      * or the currentIndex being set to currentIndex-1 if not available.
0600      *
0601      * @param event Optional, an event that will be accepted if a page is successfully
0602      * "backed" on
0603      */
0604     function goBack(event = null) {
0605         const backEvent = {accepted: false}
0606 
0607         if (layersStack.depth >= 1) {
0608             try { // app code might be screwy, but we still want to continue functioning if it throws an exception
0609                 layersStack.currentItem.backRequested(backEvent)
0610             } catch (error) {}
0611 
0612             if (!backEvent.accepted) {
0613                 if (layersStack.depth > 1) {
0614                     layersStack.pop()
0615                     if (event) event.accepted = true
0616                     return
0617                 }
0618             }
0619         }
0620 
0621         if (root.currentIndex >= 1) {
0622             try { // app code might be screwy, but we still want to continue functioning if it throws an exception
0623                 root.currentItem.backRequested(backEvent)
0624             } catch (error) {}
0625 
0626             if (!backEvent.accepted) {
0627                 if (root.depth > 1) {
0628                     root.currentIndex = Math.max(0, root.currentIndex - 1)
0629                     if (event) event.accepted = true
0630                 }
0631             }
0632         }
0633     }
0634 
0635     /**
0636      * Acts as if you had pressed the "forward" shortcut on desktop,
0637      * "going forward" in the page row. Results in the active page
0638      * becoming the next page in the row from the current active page,
0639      * i.e. currentIndex + 1.
0640      */
0641     function goForward() {
0642         root.currentIndex = Math.min(root.depth-1, root.currentIndex + 1)
0643     }
0644 //END FUNCTIONS
0645 
0646 //BEGIN signals & signal handlers
0647     /**
0648      * @brief Emitted when a page has been inserted anywhere.
0649      * @param position where the page has been inserted
0650      * @param page the new page
0651      * @since org.kde.kirigami 2.7
0652      */
0653     signal pageInserted(int position, Item page)
0654 
0655     /**
0656      * @brief Emitted when a page has been pushed to the bottom.
0657      * @param page the new page
0658      * @since org.kde.kirigami 2.5
0659      */
0660     signal pagePushed(Item page)
0661 
0662     /**
0663      * @brief Emitted when a page has been removed from the row.
0664      * @param page the page that has been removed: at this point it's still valid,
0665      *           but may be auto deleted soon.
0666      * @since org.kde.kirigami 2.5
0667      */
0668     signal pageRemoved(Item page)
0669 
0670     onLeftSidebarChanged: {
0671         if (leftSidebar && !leftSidebar.modal) {
0672             modalConnection.onModalChanged();
0673         }
0674     }
0675 
0676     Keys.onReleased: {
0677         if (event.key === Qt.Key_Back) {
0678             this.goBack(event)
0679         }
0680     }
0681 
0682     onInitialPageChanged: {
0683         if (initialPage) {
0684             clear();
0685             push(initialPage, null)
0686         }
0687     }
0688 /*
0689     onActiveFocusChanged:  {
0690         if (activeFocus) {
0691             layersStack.currentItem.forceActiveFocus()
0692             if (columnView.activeFocus) {
0693                 print("SSS"+columnView.currentItem)
0694                 columnView.currentItem.forceActiveFocus();
0695             }
0696         }
0697     }
0698 */
0699 //END signals & signal handlers
0700 
0701     Connections {
0702         id: modalConnection
0703         target: root.leftSidebar
0704         function onModalChanged() {
0705             if (leftSidebar.modal) {
0706                 const sidebar = sidebarControl.contentItem;
0707                 const background = sidebarControl.background;
0708                 sidebarControl.contentItem = null;
0709                 leftSidebar.contentItem = sidebar;
0710                 sidebarControl.background = null;
0711                 leftSidebar.background = background;
0712 
0713                 sidebar.visible = true;
0714                 background.visible = true;
0715             } else {
0716                 const sidebar = leftSidebar.contentItem
0717                 const background = leftSidebar.background
0718                 leftSidebar.contentItem=null
0719                 sidebarControl.contentItem = sidebar
0720                 leftSidebar.background=null
0721                 sidebarControl.background = background
0722 
0723                 sidebar.visible = true;
0724                 background.visible = true;
0725             }
0726         }
0727     }
0728 
0729     implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding
0730     implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
0731 
0732     Shortcut {
0733         sequences: [ StandardKey.Back ]
0734         onActivated: root.goBack()
0735     }
0736     Shortcut {
0737         sequences: [ StandardKey.Forward ]
0738         onActivated: root.goForward()
0739     }
0740 
0741     Keys.forwardTo: [currentItem]
0742 
0743     GlobalToolBar.PageRowGlobalToolBarStyleGroup {
0744         id: globalToolBar
0745         readonly property int leftReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.leftReservedSpace : 0
0746         readonly property int rightReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.rightReservedSpace : 0
0747         readonly property int height: globalToolBarUI.height
0748         readonly property Item leftHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.leftHandleAnchor : null
0749         readonly property Item rightHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.rightHandleAnchor : null
0750     }
0751 
0752     QQC2.StackView {
0753         id: layersStack
0754         z: 99
0755         anchors {
0756             fill: parent
0757         }
0758         // placeholder as initial item
0759         initialItem: columnViewLayout
0760 
0761         function clear() {
0762             // don't let it kill the main page row
0763             const d = layersStack.depth;
0764             for (let i = 1; i < d; ++i) {
0765                 pop();
0766             }
0767         }
0768 
0769         popEnter: Transition {
0770             OpacityAnimator {
0771                 from: 0
0772                 to: 1
0773                 duration: Kirigami.Units.longDuration
0774                 easing.type: Easing.InOutCubic
0775             }
0776         }
0777         popExit: Transition {
0778             ParallelAnimation {
0779                 OpacityAnimator {
0780                     from: 1
0781                     to: 0
0782                     duration: Kirigami.Units.longDuration
0783                     easing.type: Easing.InOutCubic
0784                 }
0785                 YAnimator {
0786                     from: 0
0787                     to: height/2
0788                     duration: Kirigami.Units.longDuration
0789                     easing.type: Easing.InCubic
0790                 }
0791             }
0792         }
0793 
0794         pushEnter: Transition {
0795             ParallelAnimation {
0796                 // NOTE: It's a PropertyAnimation instead of an Animator because with an animator the item will be visible for an instant before starting to fade
0797                 PropertyAnimation {
0798                     property: "opacity"
0799                     from: 0
0800                     to: 1
0801                     duration: Kirigami.Units.longDuration
0802                     easing.type: Easing.InOutCubic
0803                 }
0804                 YAnimator {
0805                     from: height/2
0806                     to: 0
0807                     duration: Kirigami.Units.longDuration
0808                     easing.type: Easing.OutCubic
0809                 }
0810             }
0811         }
0812 
0813 
0814         pushExit: Transition {
0815             OpacityAnimator {
0816                 from: 1
0817                 to: 0
0818                 duration: Kirigami.Units.longDuration
0819                 easing.type: Easing.InOutCubic
0820             }
0821         }
0822 
0823         replaceEnter: Transition {
0824             ParallelAnimation {
0825                 OpacityAnimator {
0826                     from: 0
0827                     to: 1
0828                     duration: Kirigami.Units.longDuration
0829                     easing.type: Easing.InOutCubic
0830                 }
0831                 YAnimator {
0832                     from: height/2
0833                     to: 0
0834                     duration: Kirigami.Units.longDuration
0835                     easing.type: Easing.OutCubic
0836                 }
0837             }
0838         }
0839 
0840         replaceExit: Transition {
0841             ParallelAnimation {
0842                 OpacityAnimator {
0843                     from: 1
0844                     to: 0
0845                     duration: Kirigami.Units.longDuration
0846                     easing.type: Easing.InCubic
0847                 }
0848                 YAnimator {
0849                     from: 0
0850                     to: -height/2
0851                     duration: Kirigami.Units.longDuration
0852                     easing.type: Easing.InOutCubic
0853                 }
0854             }
0855         }
0856     }
0857 
0858     Loader {
0859         id: globalToolBarUI
0860         anchors {
0861             left: parent.left
0862             top: parent.top
0863             right: parent.right
0864         }
0865         z: 100
0866         property QT.Control pageRow: root
0867         active: (!firstVisibleItem || firstVisibleItem.globalToolBarStyle !== Kirigami.ApplicationHeaderStyle.None) &&
0868                 (globalToolBar.actualStyle !== Kirigami.ApplicationHeaderStyle.None || (firstVisibleItem && firstVisibleItem.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar))
0869         visible: active
0870         height: active ? implicitHeight : 0
0871         // If load is asynchronous, it will fail to compute the initial implicitHeight
0872         // https://bugs.kde.org/show_bug.cgi?id=442660
0873         asynchronous: false
0874         source: Qt.resolvedUrl("private/globaltoolbar/PageRowGlobalToolBarUI.qml");
0875     }
0876 
0877     QtObject {
0878         id: pagesLogic
0879         readonly property var componentCache: new Array()
0880 
0881         function getPageComponent(page) {
0882             let pageComp;
0883 
0884             if (page.createObject) {
0885                 // page defined as component
0886                 pageComp = page;
0887             } else if (typeof page === "string") {
0888                 // page defined as string (a url)
0889                 pageComp = pagesLogic.componentCache[page];
0890                 if (!pageComp) {
0891                     pageComp = pagesLogic.componentCache[page] = Qt.createComponent(page);
0892                 }
0893             } else if (typeof page === "object" && !(page instanceof Item) && page.toString !== undefined) {
0894                 // page defined as url (QML value type, not a string)
0895                 pageComp = pagesLogic.componentCache[page.toString()];
0896                 if (!pageComp) {
0897                     pageComp = pagesLogic.componentCache[page.toString()] = Qt.createComponent(page.toString());
0898                 }
0899             }
0900 
0901             return pageComp
0902         }
0903 
0904         function initPage(page, properties) {
0905             const pageComp = getPageComponent(page, properties);
0906 
0907             if (pageComp) {
0908                 // instantiate page from component
0909                 // FIXME: parent directly to columnView or root?
0910                 page = pageComp.createObject(null, properties || {});
0911 
0912                 if (pageComp.status === Component.Error) {
0913                     throw new Error("Error while loading page: " + pageComp.errorString());
0914                 }
0915             } else {
0916                 // copy properties to the page
0917                 for (const prop in properties) {
0918                     if (properties.hasOwnProperty(prop)) {
0919                         page[prop] = properties[prop];
0920                     }
0921                 }
0922             }
0923             return page;
0924         }
0925 
0926         function initAndInsertPage(position, page, properties) {
0927             page = initPage(page, properties);
0928             columnView.insertItem(position, page);
0929             return page;
0930         }
0931     }
0932 
0933     RowLayout {
0934         id: columnViewLayout
0935         spacing: 1
0936         readonly property alias columnView: columnView
0937         QQC2.Control {
0938             id: sidebarControl
0939             Layout.fillHeight: true
0940             visible: contentItem !== null && root.leftDrawer && root.leftDrawer.visible
0941             leftPadding: root.leftSidebar ? root.leftSidebar.leftPadding : 0
0942             topPadding: root.leftSidebar ? root.leftSidebar.topPadding : 0
0943             rightPadding: root.leftSidebar ? root.leftSidebar.rightPadding : 0
0944             bottomPadding: root.leftSidebar ? root.leftSidebar.bottomPadding : 0
0945         }
0946         Kirigami.ColumnView {
0947             id: columnView
0948             Layout.fillWidth: true
0949             Layout.fillHeight: true
0950 
0951             topPadding: globalToolBarUI.item && globalToolBarUI.item.breadcrumbVisible
0952                         ? globalToolBarUI.height : 0
0953 
0954             // Internal hidden api for Page
0955             readonly property Item __pageRow: root
0956             acceptsMouse: Kirigami.Settings.isMobile
0957             columnResizeMode: root.wideMode ? Kirigami.ColumnView.FixedColumns : Kirigami.ColumnView.SingleColumn
0958             columnWidth: root.defaultColumnWidth
0959 
0960             onItemInserted: root.pageInserted(position, item);
0961             onItemRemoved: root.pageRemoved(item);
0962 
0963             onVisibleItemsChanged: {
0964                 // implementation of `popHiddenPages` option
0965                 if (root.popHiddenPages) {
0966                     // manually fetch lastItem here rather than use root.lastItem property, since that binding may not have updated yet
0967                     let lastItem = columnView.contentChildren[columnView.contentChildren.length - 1];
0968                     let lastVisibleItem = columnView.lastVisibleItem;
0969                     
0970                     // pop every page that isn't visible and at the top of the stack
0971                     while (lastItem && columnView.lastVisibleItem && 
0972                         lastItem !== columnView.lastVisibleItem && columnView.containsItem(lastItem)) {
0973                         root.pop();
0974                     }
0975                 }
0976             }
0977         }
0978     }
0979 
0980     Rectangle {
0981         anchors.bottom: parent.bottom
0982         height: Kirigami.Units.smallSpacing
0983         x: (columnView.width - width) * (columnView.contentX / (columnView.contentWidth - columnView.width))
0984         width: columnView.width * (columnView.width/columnView.contentWidth)
0985         color: Kirigami.Theme.textColor
0986         opacity: 0
0987         onXChanged: {
0988             opacity = 0.3
0989             scrollIndicatorTimer.restart();
0990         }
0991         Behavior on opacity {
0992             OpacityAnimator {
0993                 duration: Kirigami.Units.longDuration
0994                 easing.type: Easing.InOutQuad
0995             }
0996         }
0997         Timer {
0998             id: scrollIndicatorTimer
0999             interval: Kirigami.Units.longDuration * 4
1000             onTriggered: parent.opacity = 0;
1001         }
1002     }
1003 }