Slider.qml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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. /*!
  44. \qmltype Slider
  45. \inqmlmodule QtQuick.Controls
  46. \since 5.1
  47. \ingroup controls
  48. \brief Provides a vertical or horizontal slider control.
  49. \image slider.png
  50. The slider is the classic control for providing a bounded value. It lets
  51. the user move a slider handle along a horizontal or vertical groove
  52. and translates the handle's position into a value within the legal range.
  53. \code
  54. Slider {
  55. value: 0.5
  56. }
  57. \endcode
  58. The slider value is by default in the range [0, 1]. If integer values are
  59. needed, you can set the \l stepSize.
  60. You can create a custom appearance for a Slider by
  61. assigning a \l {SliderStyle}.
  62. */
  63. Control {
  64. id: slider
  65. /*!
  66. \qmlproperty enumeration Slider::orientation
  67. This property holds the layout orientation of the slider.
  68. The default value is \c Qt.Horizontal.
  69. */
  70. property int orientation: Qt.Horizontal
  71. /*!
  72. \qmlproperty real Slider::minimumValue
  73. This property holds the minimum value of the slider.
  74. The default value is \c{0.0}.
  75. */
  76. property alias minimumValue: range.minimumValue
  77. /*!
  78. \qmlproperty real Slider::maximumValue
  79. This property holds the maximum value of the slider.
  80. The default value is \c{1.0}.
  81. */
  82. property alias maximumValue: range.maximumValue
  83. /*!
  84. \qmlproperty bool Slider::updateValueWhileDragging
  85. This property indicates whether the current \l value should be updated while
  86. the user is moving the slider handle, or only when the button has been released.
  87. This property could for instance be modified if changing the slider value would turn
  88. out to be too time consuming.
  89. The default value is \c true.
  90. */
  91. property bool updateValueWhileDragging: true
  92. /*!
  93. \qmlproperty bool Slider::pressed
  94. This property indicates whether the slider handle is being pressed.
  95. */
  96. readonly property alias pressed: mouseArea.pressed
  97. /*!
  98. \qmlproperty bool Slider::hovered
  99. This property indicates whether the slider handle is being hovered.
  100. */
  101. readonly property alias hovered: mouseArea.handleHovered
  102. /*!
  103. \qmlproperty real Slider::stepSize
  104. This property indicates the slider step size.
  105. A value of 0 indicates that the value of the slider operates in a
  106. continuous range between \l minimumValue and \l maximumValue.
  107. Any non 0 value indicates a discrete stepSize. The following example
  108. will generate a slider with integer values in the range [0-5].
  109. \qml
  110. Slider {
  111. maximumValue: 5.0
  112. stepSize: 1.0
  113. }
  114. \endqml
  115. The default value is \c{0.0}.
  116. */
  117. property alias stepSize: range.stepSize
  118. /*!
  119. \qmlproperty real Slider::value
  120. This property holds the current value of the slider.
  121. The default value is \c{0.0}.
  122. */
  123. property alias value: range.value
  124. /*!
  125. \qmlproperty bool Slider::activeFocusOnPress
  126. This property indicates whether the slider should receive active focus when
  127. pressed.
  128. */
  129. property bool activeFocusOnPress: false
  130. /*!
  131. \qmlproperty bool Slider::tickmarksEnabled
  132. This property indicates whether the slider should display tickmarks
  133. at step intervals. Tick mark spacing is calculated based on the
  134. \l stepSize property.
  135. The default value is \c false.
  136. \note This property may be ignored on some platforms when using the native style (e.g. Android).
  137. */
  138. property bool tickmarksEnabled: false
  139. /*!
  140. \qmlproperty bool Slider::wheelEnabled
  141. This property determines whether the control handles wheel events.
  142. The default value is \c true.
  143. \since QtQuick.Controls 1.6
  144. */
  145. property alias wheelEnabled: wheelarea.enabled
  146. /*! \internal */
  147. property bool __horizontal: orientation === Qt.Horizontal
  148. /*! \internal
  149. The extra arguments positionAtMinimum and positionAtMaximum are there to force
  150. re-evaluation of the handle position when the constraints change (QTBUG-41255),
  151. and the same for range.minimumValue (QTBUG-51765) and range.maximumValue (QTBUG-63354).
  152. */
  153. property real __handlePos: range.valueForPosition(__horizontal ? fakeHandle.x : fakeHandle.y,
  154. range.positionAtMinimum, range.positionAtMaximum, range.minimumValue, range.maximumValue)
  155. activeFocusOnTab: true
  156. Accessible.role: Accessible.Slider
  157. /*! \internal */
  158. function accessibleIncreaseAction() {
  159. range.increaseSingleStep()
  160. }
  161. /*! \internal */
  162. function accessibleDecreaseAction() {
  163. range.decreaseSingleStep()
  164. }
  165. style: Settings.styleComponent(Settings.style, "SliderStyle.qml", slider)
  166. Keys.onRightPressed: if (__horizontal) range.increaseSingleStep()
  167. Keys.onLeftPressed: if (__horizontal) range.decreaseSingleStep()
  168. Keys.onUpPressed: if (!__horizontal) range.increaseSingleStep()
  169. Keys.onDownPressed: if (!__horizontal) range.decreaseSingleStep()
  170. RangeModel {
  171. id: range
  172. minimumValue: 0.0
  173. maximumValue: 1.0
  174. value: 0
  175. stepSize: 0.0
  176. inverted: __horizontal ? false : true
  177. positionAtMinimum: 0
  178. positionAtMaximum: __horizontal ? slider.width - fakeHandle.width : slider.height - fakeHandle.height
  179. }
  180. Item {
  181. id: fakeHandle
  182. anchors.verticalCenter: __horizontal ? parent.verticalCenter : undefined
  183. anchors.horizontalCenter: !__horizontal ? parent.horizontalCenter : undefined
  184. width: __panel.handleWidth
  185. height: __panel.handleHeight
  186. function updatePos() {
  187. if (updateValueWhileDragging && !mouseArea.drag.active)
  188. range.position = __horizontal ? x : y
  189. }
  190. onXChanged: updatePos();
  191. onYChanged: updatePos();
  192. }
  193. MouseArea {
  194. id: mouseArea
  195. anchors.fill: parent
  196. hoverEnabled: Settings.hoverEnabled
  197. property int clickOffset: 0
  198. property real pressX: 0
  199. property real pressY: 0
  200. property bool handleHovered: false
  201. function clamp ( val ) {
  202. return Math.max(range.positionAtMinimum, Math.min(range.positionAtMaximum, val))
  203. }
  204. function updateHandlePosition(mouse, force) {
  205. var pos, overThreshold
  206. if (__horizontal) {
  207. pos = clamp (mouse.x + clickOffset - fakeHandle.width/2)
  208. overThreshold = Math.abs(mouse.x - pressX) >= Settings.dragThreshold
  209. if (overThreshold)
  210. preventStealing = true
  211. if (overThreshold || force)
  212. fakeHandle.x = pos
  213. } else if (!__horizontal) {
  214. pos = clamp (mouse.y + clickOffset- fakeHandle.height/2)
  215. overThreshold = Math.abs(mouse.y - pressY) >= Settings.dragThreshold
  216. if (overThreshold)
  217. preventStealing = true
  218. if (overThreshold || force)
  219. fakeHandle.y = pos
  220. }
  221. }
  222. onPositionChanged: {
  223. if (pressed)
  224. updateHandlePosition(mouse, !Settings.hasTouchScreen || preventStealing)
  225. var point = mouseArea.mapToItem(fakeHandle, mouse.x, mouse.y)
  226. handleHovered = fakeHandle.contains(Qt.point(point.x, point.y))
  227. }
  228. onPressed: {
  229. if (slider.activeFocusOnPress)
  230. slider.forceActiveFocus();
  231. if (handleHovered) {
  232. var point = mouseArea.mapToItem(fakeHandle, mouse.x, mouse.y)
  233. clickOffset = __horizontal ? fakeHandle.width/2 - point.x : fakeHandle.height/2 - point.y
  234. }
  235. pressX = mouse.x
  236. pressY = mouse.y
  237. updateHandlePosition(mouse, !Settings.hasTouchScreen)
  238. }
  239. onReleased: {
  240. updateHandlePosition(mouse, Settings.hasTouchScreen)
  241. // If we don't update while dragging, this is the only
  242. // moment that the range is updated.
  243. if (!slider.updateValueWhileDragging)
  244. range.position = __horizontal ? fakeHandle.x : fakeHandle.y;
  245. clickOffset = 0
  246. preventStealing = false
  247. }
  248. onExited: handleHovered = false
  249. }
  250. // During the drag, we simply ignore the position set from the range, this
  251. // means that setting a value while dragging will not "interrupt" the
  252. // dragging activity.
  253. Qml.Binding {
  254. when: !mouseArea.drag.active
  255. target: fakeHandle
  256. property: __horizontal ? "x" : "y"
  257. value: range.position
  258. restoreMode: Binding.RestoreBinding
  259. }
  260. WheelArea {
  261. id: wheelarea
  262. anchors.fill: parent
  263. verticalValue: slider.value
  264. horizontalValue: slider.value
  265. horizontalMinimumValue: slider.minimumValue
  266. horizontalMaximumValue: slider.maximumValue
  267. verticalMinimumValue: slider.minimumValue
  268. verticalMaximumValue: slider.maximumValue
  269. property real step: (slider.maximumValue - slider.minimumValue)/(range.positionAtMaximum - range.positionAtMinimum)
  270. onVerticalWheelMoved: {
  271. if (verticalDelta !== 0) {
  272. var delta = Math.abs(verticalDelta)*step > stepSize ? verticalDelta*step : verticalDelta/Math.abs(verticalDelta)*stepSize
  273. range.position = range.positionForValue(value - delta * (inverted ? 1 : -1))
  274. }
  275. }
  276. onHorizontalWheelMoved: {
  277. if (horizontalDelta !== 0) {
  278. var delta = Math.abs(horizontalDelta)*step > stepSize ? horizontalDelta*step : horizontalDelta/Math.abs(horizontalDelta)*stepSize
  279. range.position = range.positionForValue(value + delta * (inverted ? 1 : -1))
  280. }
  281. }
  282. }
  283. }