ScrollView.qml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2016 The Qt Company Ltd.
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of the Qt Quick Controls module of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:LGPL$
  9. ** Commercial License Usage
  10. ** Licensees holding valid commercial Qt licenses may use this file in
  11. ** accordance with the commercial license agreement provided with the
  12. ** Software or, alternatively, in accordance with the terms contained in
  13. ** a written agreement between you and The Qt Company. For licensing terms
  14. ** and conditions see https://www.qt.io/terms-conditions. For further
  15. ** information use the contact form at https://www.qt.io/contact-us.
  16. **
  17. ** GNU Lesser General Public License Usage
  18. ** Alternatively, this file may be used under the terms of the GNU Lesser
  19. ** General Public License version 3 as published by the Free Software
  20. ** Foundation and appearing in the file LICENSE.LGPL3 included in the
  21. ** packaging of this file. Please review the following information to
  22. ** ensure the GNU Lesser General Public License version 3 requirements
  23. ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 2.0 or (at your option) the GNU General
  28. ** Public license version 3 or any later version approved by the KDE Free
  29. ** Qt Foundation. The licenses are as published by the Free Software
  30. ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
  31. ** included in the packaging of this file. Please review the following
  32. ** information to ensure the GNU General Public License requirements will
  33. ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
  34. ** https://www.gnu.org/licenses/gpl-3.0.html.
  35. **
  36. ** $QT_END_LICENSE$
  37. **
  38. ****************************************************************************/
  39. import QtQml 2.14 as Qml
  40. import QtQuick 2.2
  41. import QtQuick.Controls 1.2
  42. import QtQuick.Controls.Private 1.0
  43. import QtQuick.Controls.Styles 1.1
  44. /*!
  45. \qmltype ScrollView
  46. \inqmlmodule QtQuick.Controls
  47. \since 5.1
  48. \ingroup views
  49. \ingroup controls
  50. \brief Provides a scrolling view within another Item.
  51. \image scrollview.png
  52. A ScrollView can be used either to replace a \l Flickable or decorate an
  53. existing \l Flickable. Depending on the platform, it will add scroll bars and
  54. a content frame.
  55. Only one Item can be a direct child of the ScrollView and the child is implicitly anchored
  56. to fill the scroll view.
  57. Example:
  58. \code
  59. ScrollView {
  60. Image { source: "largeImage.png" }
  61. }
  62. \endcode
  63. In the previous example the Image item will implicitly get scroll behavior as if it was
  64. used within a \l Flickable. The width and height of the child item will be used to
  65. define the size of the content area.
  66. Example:
  67. \code
  68. ScrollView {
  69. ListView {
  70. ...
  71. }
  72. }
  73. \endcode
  74. In this case the content size of the ScrollView will simply mirror that of its contained
  75. \l flickableItem.
  76. You can create a custom appearance for a ScrollView by
  77. assigning a \l {ScrollViewStyle}.
  78. */
  79. FocusScope {
  80. id: root
  81. implicitWidth: 240
  82. implicitHeight: 150
  83. /*!
  84. This property tells the ScrollView if it should render
  85. a frame around its content.
  86. The default value is \c false.
  87. */
  88. property bool frameVisible: false
  89. /*! \qmlproperty enumeration ScrollView::horizontalScrollBarPolicy
  90. \since QtQuick.Controls 1.3
  91. This property holds the policy for showing the horizontal scrollbar.
  92. It can be any of the following values:
  93. \list
  94. \li Qt.ScrollBarAsNeeded
  95. \li Qt.ScrollBarAlwaysOff
  96. \li Qt.ScrollBarAlwaysOn
  97. \endlist
  98. The default policy is \c Qt.ScrollBarAsNeeded.
  99. */
  100. property alias horizontalScrollBarPolicy: scroller.horizontalScrollBarPolicy
  101. /*! \qmlproperty enumeration ScrollView::verticalScrollBarPolicy
  102. \since QtQuick.Controls 1.3
  103. This property holds the policy for showing the vertical scrollbar.
  104. It can be any of the following values:
  105. \list
  106. \li Qt.ScrollBarAsNeeded
  107. \li Qt.ScrollBarAlwaysOff
  108. \li Qt.ScrollBarAlwaysOn
  109. \endlist
  110. The default policy is \c Qt.ScrollBarAsNeeded.
  111. */
  112. property alias verticalScrollBarPolicy: scroller.verticalScrollBarPolicy
  113. /*!
  114. This property controls if there should be a highlight
  115. around the frame when the ScrollView has input focus.
  116. The default value is \c false.
  117. \note This property is only applicable on some platforms, such
  118. as Mac OS.
  119. */
  120. property bool highlightOnFocus: false
  121. /*!
  122. \qmlproperty Item ScrollView::viewport
  123. The viewport determines the current "window" on the contentItem.
  124. In other words, it clips it and the size of the viewport tells you
  125. how much of the content area is visible.
  126. */
  127. property alias viewport: viewportItem
  128. /*!
  129. \qmlproperty Item ScrollView::flickableItem
  130. The flickableItem of the ScrollView. If the contentItem provided
  131. to the ScrollView is a Flickable, it will be the \l contentItem.
  132. */
  133. readonly property alias flickableItem: internal.flickableItem
  134. /*!
  135. The contentItem of the ScrollView. This is set by the user.
  136. Note that the definition of contentItem is somewhat different to that
  137. of a Flickable, where the contentItem is implicitly created.
  138. */
  139. default property Item contentItem
  140. /*! \internal */
  141. property alias __scroller: scroller
  142. /*! \internal */
  143. property alias __verticalScrollbarOffset: scroller.verticalScrollbarOffset
  144. /*! \internal */
  145. property alias __wheelAreaScrollSpeed: wheelArea.scrollSpeed
  146. /*! \internal */
  147. property int __scrollBarTopMargin: 0
  148. /*! \internal */
  149. property int __viewTopMargin: 0
  150. /*! \internal */
  151. property alias __horizontalScrollBar: scroller.horizontalScrollBar
  152. /*! \internal */
  153. property alias __verticalScrollBar: scroller.verticalScrollBar
  154. /*! \qmlproperty Component ScrollView::style
  155. The style Component for this control.
  156. \sa {Qt Quick Controls 1 Styles QML Types}
  157. */
  158. property Component style: Settings.styleComponent(Settings.style, "ScrollViewStyle.qml", root)
  159. /*! \internal */
  160. property Style __style: styleLoader.item
  161. activeFocusOnTab: false
  162. onContentItemChanged: {
  163. if (contentItem.hasOwnProperty("contentY") && // Check if flickable
  164. contentItem.hasOwnProperty("contentHeight")) {
  165. internal.flickableItem = contentItem // "Use content if it is a flickable
  166. internal.flickableItem.parent = viewportItem
  167. } else {
  168. internal.flickableItem = flickableComponent.createObject(viewportItem)
  169. contentItem.parent = internal.flickableItem.contentItem
  170. }
  171. internal.flickableItem.anchors.fill = viewportItem
  172. if (!Settings.hasTouchScreen)
  173. internal.flickableItem.interactive = false
  174. }
  175. children: Item {
  176. id: internal
  177. property Flickable flickableItem
  178. Loader {
  179. id: styleLoader
  180. sourceComponent: style
  181. onStatusChanged: {
  182. if (status === Loader.Error)
  183. console.error("Failed to load Style for", root)
  184. }
  185. property alias __control: root
  186. }
  187. Qml.Binding {
  188. target: flickableItem
  189. property: "contentHeight"
  190. when: contentItem !== flickableItem
  191. value: contentItem ? contentItem.height : 0
  192. restoreMode: Binding.RestoreBinding
  193. }
  194. Qml.Binding {
  195. target: flickableItem
  196. when: contentItem !== flickableItem
  197. property: "contentWidth"
  198. value: contentItem ? contentItem.width : 0
  199. restoreMode: Binding.RestoreBinding
  200. }
  201. Connections {
  202. target: flickableItem
  203. function onContentYChanged() {
  204. scroller.blockUpdates = true
  205. scroller.verticalScrollBar.value = flickableItem.contentY - flickableItem.originY
  206. scroller.blockUpdates = false
  207. }
  208. function onContentXChanged() {
  209. scroller.blockUpdates = true
  210. scroller.horizontalScrollBar.value = flickableItem.contentX - flickableItem.originX
  211. scroller.blockUpdates = false
  212. }
  213. }
  214. anchors.fill: parent
  215. Component {
  216. id: flickableComponent
  217. Flickable {}
  218. }
  219. WheelArea {
  220. id: wheelArea
  221. parent: flickableItem
  222. z: -1
  223. // ### Note this is needed due to broken mousewheel behavior in Flickable.
  224. anchors.fill: parent
  225. property int acceleration: 40
  226. property int flickThreshold: Settings.dragThreshold
  227. property real speedThreshold: 3
  228. property real ignored: 0.001 // ## flick() does not work with 0 yVelocity
  229. property int maxFlick: 400
  230. property bool horizontalRecursionGuard: false
  231. property bool verticalRecursionGuard: false
  232. horizontalMinimumValue: 0
  233. horizontalMaximumValue: flickableItem ? flickableItem.contentWidth - viewport.width : 0
  234. onHorizontalMaximumValueChanged: {
  235. wheelArea.horizontalRecursionGuard = true
  236. //if horizontalMaximumValue changed, horizontalValue may be actually synced with
  237. wheelArea.horizontalValue = flickableItem.contentX - flickableItem.originX;
  238. wheelArea.horizontalRecursionGuard = false
  239. }
  240. verticalMinimumValue: 0
  241. verticalMaximumValue: flickableItem ? flickableItem.contentHeight - viewport.height + __viewTopMargin : 0
  242. onVerticalMaximumValueChanged: {
  243. wheelArea.verticalRecursionGuard = true
  244. //if verticalMaximumValue changed, verticalValue may be actually synced with
  245. wheelArea.verticalValue = flickableItem.contentY - flickableItem.originY;
  246. wheelArea.verticalRecursionGuard = false
  247. }
  248. // The default scroll speed for typical angle-based mouse wheels. The value
  249. // comes originally from QTextEdit, which sets 20px steps by default, as well as
  250. // QQuickWheelArea.
  251. // TODO: centralize somewhere, QPlatformTheme?
  252. scrollSpeed: 20 * (__style && __style.__wheelScrollLines || 1)
  253. Connections {
  254. target: flickableItem
  255. function onContentYChanged() {
  256. wheelArea.verticalRecursionGuard = true
  257. wheelArea.verticalValue = flickableItem.contentY - flickableItem.originY
  258. wheelArea.verticalRecursionGuard = false
  259. }
  260. function onContentXChanged() {
  261. wheelArea.horizontalRecursionGuard = true
  262. wheelArea.horizontalValue = flickableItem.contentX - flickableItem.originX
  263. wheelArea.horizontalRecursionGuard = false
  264. }
  265. }
  266. onVerticalValueChanged: {
  267. if (!verticalRecursionGuard) {
  268. var effectiveContentY = flickableItem.contentY - flickableItem.originY
  269. if (effectiveContentY < flickThreshold && verticalDelta > speedThreshold) {
  270. flickableItem.flick(ignored, Math.min(maxFlick, acceleration * verticalDelta))
  271. } else if (effectiveContentY > flickableItem.contentHeight - flickThreshold - viewport.height
  272. && verticalDelta < -speedThreshold) {
  273. flickableItem.flick(ignored, Math.max(-maxFlick, acceleration * verticalDelta))
  274. } else {
  275. flickableItem.contentY = verticalValue + flickableItem.originY
  276. }
  277. }
  278. }
  279. onHorizontalValueChanged: {
  280. if (!horizontalRecursionGuard)
  281. flickableItem.contentX = horizontalValue + flickableItem.originX
  282. }
  283. }
  284. ScrollViewHelper {
  285. id: scroller
  286. anchors.fill: parent
  287. active: wheelArea.active
  288. property bool outerFrame: !frameVisible || !(__style ? __style.__externalScrollBars : 0)
  289. property int scrollBarSpacing: outerFrame ? 0 : (__style ? __style.__scrollBarSpacing : 0)
  290. property int verticalScrollbarOffset: verticalScrollBar.visible && !verticalScrollBar.isTransient ?
  291. verticalScrollBar.width + scrollBarSpacing : 0
  292. property int horizontalScrollbarOffset: horizontalScrollBar.visible && !horizontalScrollBar.isTransient ?
  293. horizontalScrollBar.height + scrollBarSpacing : 0
  294. Loader {
  295. id: frameLoader
  296. sourceComponent: __style ? __style.frame : null
  297. anchors.fill: parent
  298. anchors.rightMargin: scroller.outerFrame ? 0 : scroller.verticalScrollbarOffset
  299. anchors.bottomMargin: scroller.outerFrame ? 0 : scroller.horizontalScrollbarOffset
  300. }
  301. Item {
  302. id: viewportItem
  303. anchors.fill: frameLoader
  304. anchors.topMargin: frameVisible ? __style.padding.top : 0
  305. anchors.leftMargin: frameVisible ? __style.padding.left : 0
  306. anchors.rightMargin: (frameVisible ? __style.padding.right : 0) + (scroller.outerFrame ? scroller.verticalScrollbarOffset : 0)
  307. anchors.bottomMargin: (frameVisible ? __style.padding.bottom : 0) + (scroller.outerFrame ? scroller.horizontalScrollbarOffset : 0)
  308. clip: true
  309. }
  310. }
  311. FocusFrame { visible: highlightOnFocus && root.activeFocus }
  312. }
  313. }