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 }