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