Warning, /frameworks/kirigami/src/controls/ScrollablePage.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 import QtQuick 0008 import QtQml 0009 import QtQuick.Controls as QQC2 0010 import org.kde.kirigami as Kirigami 0011 import org.kde.kirigami.templates as KT 0012 import "private" 0013 0014 0015 // TODO KF6: undo many workarounds to make existing code work? 0016 0017 /** 0018 * @brief ScrollablePage is a Page that holds scrollable content, such as a ListView. 0019 * 0020 * Scrolling and scrolling indicators will be automatically managed. 0021 * 0022 * Example usage: 0023 * @code 0024 * ScrollablePage { 0025 * id: root 0026 * // The page will automatically be scrollable 0027 * Rectangle { 0028 * width: root.width 0029 * height: 99999 0030 * } 0031 * } 0032 * @endcode 0033 * 0034 * @warning Do not put a ScrollView inside of a ScrollablePage; children of a ScrollablePage are already inside a ScrollView. 0035 * 0036 * Another behavior added by this class is a "scroll down to refresh" behavior 0037 * It also can give the contents of the flickable to have more top margins in order 0038 * to make possible to scroll down the list to reach it with the thumb while using the 0039 * phone with a single hand. 0040 * 0041 * Implementations should handle the refresh themselves as follows 0042 * 0043 * Example usage: 0044 * @code 0045 * Kirigami.ScrollablePage { 0046 * id: view 0047 * supportsRefreshing: true 0048 * onRefreshingChanged: { 0049 * if (refreshing) { 0050 * myModel.refresh(); 0051 * } 0052 * } 0053 * ListView { 0054 * // NOTE: MyModel doesn't come from the components, 0055 * // it's purely an example on how it can be used together 0056 * // some application logic that can update the list model 0057 * // and signals when it's done. 0058 * model: MyModel { 0059 * onRefreshDone: view.refreshing = false; 0060 * } 0061 * delegate: ItemDelegate {} 0062 * } 0063 * } 0064 * [...] 0065 * @endcode 0066 */ 0067 Kirigami.Page { 0068 id: root 0069 0070 //BEGIN properties 0071 /** 0072 * @brief This property tells whether the list is asking for a refresh. 0073 * 0074 * This property will automatically be set to true when the user pulls the list down enough, 0075 * which in return, shows a loading spinner. When this is set to true, it signals 0076 * the application logic to start its refresh procedure. 0077 * 0078 * default: ``false`` 0079 * 0080 * @note The application itself will have to set back this property to false when done. 0081 */ 0082 property bool refreshing: false 0083 0084 /** 0085 * @brief This property sets whether scrollable page supports "pull down to refresh" behaviour. 0086 * 0087 * default: ``false`` 0088 */ 0089 property bool supportsRefreshing: false 0090 0091 /** 0092 * @brief This property holds the main Flickable item of this page. 0093 * @deprecated here for compatibility; will be removed in KF6. 0094 */ 0095 property Flickable flickable: Flickable {} // FIXME KF6: this empty flickable exists for compatibility reasons. some apps assume flickable exists right from the beginning but ScrollView internally assumes it does not 0096 onFlickableChanged: scrollView.contentItem = flickable; 0097 0098 /** 0099 * @brief This property sets the vertical scrollbar policy. 0100 * @property Qt::ScrollBarPolicy verticalScrollBarPolicy 0101 */ 0102 property int verticalScrollBarPolicy 0103 0104 /** 0105 * @brief This property sets the horizontal scrollbar policy. 0106 * @property Qt::ScrollBarPolicy horizontalScrollBarPolicy 0107 */ 0108 property int horizontalScrollBarPolicy: QQC2.ScrollBar.AlwaysOff 0109 0110 default property alias scrollablePageData: itemsParent.data 0111 property alias scrollablePageChildren: itemsParent.children 0112 0113 /* 0114 * @deprecated here for compatibility; will be removed in KF6. 0115 */ 0116 property QtObject mainItem 0117 onMainItemChanged: { 0118 print("Warning: the mainItem property is deprecated"); 0119 scrollablePageData.push(mainItem); 0120 } 0121 0122 /** 0123 * @brief This property sets whether it is possible to navigate the items in a view that support it. 0124 * 0125 * If true, and if flickable is an item view (e.g. ListView, GridView), it will be possible 0126 * to navigate the view current items with keyboard up/down arrow buttons. 0127 * Also, any key event will be forwarded to the current list item. 0128 * 0129 * default: ``true`` 0130 */ 0131 property bool keyboardNavigationEnabled: true 0132 //END properties 0133 0134 implicitWidth: flickable?.contentItem?.implicitWidth 0135 ?? Math.max(implicitBackgroundWidth + leftInset + rightInset, 0136 contentWidth + leftPadding + rightPadding, 0137 implicitHeaderWidth, 0138 implicitFooterWidth) 0139 0140 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 0141 contentHeight + topPadding + bottomPadding 0142 + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0) 0143 + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0)) 0144 0145 contentHeight: flickable?.contentHeight ?? 0 0146 0147 Kirigami.Theme.inherit: false 0148 Kirigami.Theme.colorSet: flickable?.hasOwnProperty("model") ? Kirigami.Theme.View : Kirigami.Theme.Window 0149 0150 Keys.forwardTo: { 0151 if (root.keyboardNavigationEnabled && root.flickable) { 0152 if (("currentItem" in root.flickable) && root.flickable.currentItem) { 0153 return [ root.flickable.currentItem, root.flickable ]; 0154 } else { 0155 return [ root.flickable ]; 0156 } 0157 } else { 0158 return []; 0159 } 0160 } 0161 0162 contentItem: QQC2.ScrollView { 0163 id: scrollView 0164 anchors { 0165 top: root.header?.visible 0166 ? root.header.bottom 0167 : parent.top 0168 bottom: root.footer?.visible ? root.footer.top : parent.bottom 0169 left: parent.left 0170 right: parent.right 0171 } 0172 clip: true 0173 QQC2.ScrollBar.horizontal.policy: root.horizontalScrollBarPolicy 0174 QQC2.ScrollBar.vertical.policy: root.verticalScrollBarPolicy 0175 } 0176 0177 data: [ 0178 // Has to be a MouseArea that accepts events otherwise touch events on Wayland will get lost 0179 MouseArea { 0180 id: scrollingArea 0181 width: root.horizontalScrollBarPolicy === QQC2.ScrollBar.AlwaysOff ? root.flickable.width : Math.max(root.flickable.width, implicitWidth) 0182 height: Math.max(root.flickable.height, implicitHeight) 0183 implicitWidth: { 0184 let implicit = 0; 0185 for (const child of itemsParent.visibleChildren) { 0186 if (child.implicitWidth <= 0) { 0187 implicit = Math.max(implicit, child.width); 0188 } else { 0189 implicit = Math.max(implicit, child.implicitWidth); 0190 } 0191 } 0192 return implicit + itemsParent.anchors.leftMargin + itemsParent.anchors.rightMargin; 0193 } 0194 implicitHeight: { 0195 let implicit = 0; 0196 for (const child of itemsParent.visibleChildren) { 0197 if (child.implicitHeight <= 0) { 0198 implicit = Math.max(implicit, child.height); 0199 } else { 0200 implicit = Math.max(implicit, child.implicitHeight); 0201 } 0202 } 0203 return implicit + itemsParent.anchors.topMargin + itemsParent.anchors.bottomMargin; 0204 } 0205 Item { 0206 id: itemsParent 0207 property Flickable flickable 0208 anchors { 0209 fill: parent 0210 topMargin: root.topPadding 0211 leftMargin: root.leftPadding 0212 rightMargin: root.rightPadding 0213 bottomMargin: root.bottomPadding 0214 } 0215 onChildrenChanged: { 0216 const child = children[children.length - 1]; 0217 if (child instanceof QQC2.ScrollView) { 0218 print("Warning: it's not supported to have ScrollViews inside a ScrollablePage") 0219 } 0220 } 0221 } 0222 Binding { 0223 target: root.flickable 0224 property: "bottomMargin" 0225 value: root.bottomPadding 0226 restoreMode: Binding.RestoreBinding 0227 } 0228 }, 0229 0230 Loader { 0231 id: busyIndicatorLoader 0232 active: root.supportsRefreshing 0233 sourceComponent: PullDownIndicator { 0234 parent: root 0235 active: root.refreshing 0236 onTriggered: root.refreshing = true 0237 } 0238 } 0239 ] 0240 0241 Component.onCompleted: { 0242 let flickableFound = false; 0243 for (const child of itemsParent.data) { 0244 if (child instanceof Flickable) { 0245 // If there were more flickable children, take the last one, as behavior compatibility 0246 // with old internal ScrollView 0247 child.activeFocusOnTab = true; 0248 root.flickable = child; 0249 flickableFound = true; 0250 if (child instanceof ListView) { 0251 child.keyNavigationEnabled = true; 0252 child.keyNavigationWraps = false; 0253 } 0254 } else if (child instanceof Item) { 0255 child.anchors.left = itemsParent.left; 0256 child.anchors.right = itemsParent.right; 0257 } else if (child instanceof KT.OverlaySheet) { 0258 // Reparent sheets, needs to be done before Component.onCompleted 0259 if (child.parent === itemsParent || child.parent === null) { 0260 child.parent = root; 0261 } 0262 } 0263 } 0264 0265 if (flickableFound) { 0266 scrollView.contentItem = root.flickable; 0267 root.flickable.parent = scrollView; 0268 // The flickable needs focus only if the page didn't already explicitly set focus to some other control (eg a text field in the header) 0269 Qt.callLater(() => { 0270 if (root.activeFocus) { 0271 root.flickable.forceActiveFocus(); 0272 } 0273 }); 0274 // Some existing code incorrectly uses anchors 0275 root.flickable.anchors.fill = undefined; 0276 root.flickable.anchors.top = undefined; 0277 root.flickable.anchors.left = undefined; 0278 root.flickable.anchors.right = undefined; 0279 root.flickable.anchors.bottom = undefined; 0280 scrollingArea.visible = false; 0281 } else { 0282 scrollView.contentItem = root.flickable; 0283 scrollingArea.parent = root.flickable.contentItem; 0284 scrollingArea.visible = true; 0285 root.flickable.contentHeight = Qt.binding(() => scrollingArea.implicitHeight - root.flickable.topMargin - root.flickable.bottomMargin); 0286 root.flickable.contentWidth = Qt.binding(() => scrollingArea.implicitWidth); 0287 scrollView.forceActiveFocus(Qt.TabFocusReason); // QTBUG-44043 : Focus on currentItem instead of pageStack itself 0288 } 0289 root.flickable.flickableDirection = Flickable.VerticalFlick; 0290 } 0291 }