LayerSlider.qml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. // Copyright (c) 2021 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.2
  4. import QtQuick.Layouts 1.1
  5. import UM 1.0 as UM
  6. import Cura 1.0 as Cura
  7. Item
  8. {
  9. id: sliderRoot
  10. // Handle properties
  11. property real handleSize: UM.Theme.getSize("slider_handle").width
  12. property real handleRadius: handleSize / 2
  13. property real minimumRangeHandleSize: handleSize / 2
  14. property color upperHandleColor: UM.Theme.getColor("slider_handle")
  15. property color lowerHandleColor: UM.Theme.getColor("slider_handle")
  16. property color rangeHandleColor: UM.Theme.getColor("slider_groove_fill")
  17. property color handleActiveColor: UM.Theme.getColor("slider_handle_active")
  18. property var activeHandle: upperHandle
  19. // Track properties
  20. property real trackThickness: UM.Theme.getSize("slider_groove").width // width of the slider track
  21. property real trackRadius: UM.Theme.getSize("slider_groove_radius").width
  22. property color trackColor: UM.Theme.getColor("slider_groove")
  23. // value properties
  24. property real maximumValue: 100
  25. property real minimumValue: 0
  26. property real minimumRange: 0 // minimum range allowed between min and max values
  27. property bool roundValues: true
  28. property real upperValue: maximumValue
  29. property real lowerValue: minimumValue
  30. property bool layersVisible: true
  31. property bool manuallyChanged: true // Indicates whether the value was changed manually or during simulation
  32. function getUpperValueFromSliderHandle()
  33. {
  34. return upperHandle.getValue()
  35. }
  36. function setUpperValue(value)
  37. {
  38. upperHandle.setValue(value)
  39. updateRangeHandle()
  40. }
  41. function getLowerValueFromSliderHandle()
  42. {
  43. return lowerHandle.getValue()
  44. }
  45. function setLowerValue(value)
  46. {
  47. lowerHandle.setValue(value)
  48. updateRangeHandle()
  49. }
  50. function updateRangeHandle()
  51. {
  52. rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
  53. }
  54. // set the active handle to show only one label at a time
  55. function setActiveHandle(handle)
  56. {
  57. activeHandle = handle
  58. }
  59. function normalizeValue(value)
  60. {
  61. return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
  62. }
  63. // Slider track
  64. Rectangle
  65. {
  66. id: track
  67. width: sliderRoot.trackThickness
  68. height: sliderRoot.height - sliderRoot.handleSize
  69. radius: sliderRoot.trackRadius
  70. anchors.centerIn: sliderRoot
  71. color: sliderRoot.trackColor
  72. visible: sliderRoot.layersVisible
  73. }
  74. // Range handle
  75. Item
  76. {
  77. id: rangeHandle
  78. y: upperHandle.y + upperHandle.height
  79. width: sliderRoot.handleSize
  80. height: sliderRoot.minimumRangeHandleSize
  81. anchors.horizontalCenter: sliderRoot.horizontalCenter
  82. visible: sliderRoot.layersVisible
  83. // Set the new value when dragging
  84. function onHandleDragged()
  85. {
  86. sliderRoot.manuallyChanged = true
  87. upperHandle.y = y - upperHandle.height
  88. lowerHandle.y = y + height
  89. var upperValue = sliderRoot.getUpperValueFromSliderHandle()
  90. var lowerValue = sliderRoot.getLowerValueFromSliderHandle()
  91. // set both values after moving the handle position
  92. UM.SimulationView.setCurrentLayer(upperValue)
  93. UM.SimulationView.setMinimumLayer(lowerValue)
  94. }
  95. function setValueManually(value)
  96. {
  97. sliderRoot.manuallyChanged = true
  98. upperHandle.setValue(value)
  99. }
  100. function setValue(value)
  101. {
  102. var range = sliderRoot.upperValue - sliderRoot.lowerValue
  103. value = Math.min(value, sliderRoot.maximumValue)
  104. value = Math.max(value, sliderRoot.minimumValue + range)
  105. UM.SimulationView.setCurrentLayer(value)
  106. UM.SimulationView.setMinimumLayer(value - range)
  107. }
  108. Rectangle
  109. {
  110. width: sliderRoot.trackThickness
  111. height: parent.height + sliderRoot.handleSize
  112. anchors.centerIn: parent
  113. radius: sliderRoot.trackRadius
  114. color: sliderRoot.rangeHandleColor
  115. }
  116. MouseArea
  117. {
  118. anchors.fill: parent
  119. drag
  120. {
  121. target: parent
  122. axis: Drag.YAxis
  123. minimumY: upperHandle.height
  124. maximumY: sliderRoot.height - (rangeHandle.height + lowerHandle.height)
  125. }
  126. onPositionChanged: parent.onHandleDragged()
  127. onPressed:
  128. {
  129. sliderRoot.setActiveHandle(rangeHandle)
  130. sliderRoot.forceActiveFocus()
  131. }
  132. }
  133. }
  134. onHeightChanged : {
  135. // After a height change, the pixel-position of the handles is out of sync with the property value
  136. setLowerValue(lowerValue)
  137. setUpperValue(upperValue)
  138. }
  139. // Upper handle
  140. Rectangle
  141. {
  142. id: upperHandle
  143. y: sliderRoot.height - (sliderRoot.minimumRangeHandleSize + 2 * sliderRoot.handleSize)
  144. width: sliderRoot.handleSize
  145. height: sliderRoot.handleSize
  146. anchors.horizontalCenter: sliderRoot.horizontalCenter
  147. radius: sliderRoot.handleRadius
  148. color: upperHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.upperHandleColor
  149. visible: sliderRoot.layersVisible
  150. function onHandleDragged()
  151. {
  152. sliderRoot.manuallyChanged = true
  153. // don't allow the lower handle to be higher than the upper handle
  154. if (lowerHandle.y - (y + height) < sliderRoot.minimumRangeHandleSize)
  155. {
  156. lowerHandle.y = y + height + sliderRoot.minimumRangeHandleSize
  157. }
  158. // update the range handle
  159. sliderRoot.updateRangeHandle()
  160. // set the new value after moving the handle position
  161. UM.SimulationView.setCurrentLayer(getValue())
  162. }
  163. // get the upper value based on the slider position
  164. function getValue()
  165. {
  166. var result = y / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize))
  167. result = sliderRoot.maximumValue + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumValue))
  168. result = sliderRoot.roundValues ? Math.round(result) : result
  169. return result
  170. }
  171. function setValueManually(value)
  172. {
  173. sliderRoot.manuallyChanged = true
  174. upperHandle.setValue(value)
  175. }
  176. // set the slider position based on the upper value
  177. function setValue(value)
  178. {
  179. // Normalize values between range, since using arrow keys will create out-of-the-range values
  180. value = sliderRoot.normalizeValue(value)
  181. UM.SimulationView.setCurrentLayer(value)
  182. var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
  183. // In case there is only one layer, the diff value results in a NaN, so this is for catching this specific case
  184. if (isNaN(diff))
  185. {
  186. diff = 0
  187. }
  188. var newUpperYPosition = Math.round(diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
  189. y = newUpperYPosition
  190. // update the range handle
  191. sliderRoot.updateRangeHandle()
  192. }
  193. Keys.onUpPressed: upperHandleLabel.setValue(upperHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  194. Keys.onDownPressed: upperHandleLabel.setValue(upperHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  195. // dragging
  196. MouseArea
  197. {
  198. anchors.fill: parent
  199. drag
  200. {
  201. target: parent
  202. axis: Drag.YAxis
  203. minimumY: 0
  204. maximumY: sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)
  205. }
  206. onPositionChanged: parent.onHandleDragged()
  207. onPressed:
  208. {
  209. sliderRoot.setActiveHandle(upperHandle)
  210. upperHandleLabel.forceActiveFocus()
  211. }
  212. }
  213. SimulationSliderLabel
  214. {
  215. id: upperHandleLabel
  216. height: sliderRoot.handleSize
  217. anchors.bottom: parent.top
  218. anchors.bottomMargin: UM.Theme.getSize("narrow_margin").height
  219. anchors.horizontalCenter: parent.horizontalCenter
  220. target: Qt.point(parent.width / 2, parent.top)
  221. visible: sliderRoot.activeHandle == parent || sliderRoot.activeHandle == rangeHandle
  222. // custom properties
  223. maximumValue: sliderRoot.maximumValue
  224. value: sliderRoot.upperValue
  225. busy: UM.SimulationView.busy
  226. setValue: upperHandle.setValueManually // connect callback functions
  227. }
  228. }
  229. // Lower handle
  230. Rectangle
  231. {
  232. id: lowerHandle
  233. y: sliderRoot.height - sliderRoot.handleSize
  234. width: parent.handleSize
  235. height: parent.handleSize
  236. anchors.horizontalCenter: parent.horizontalCenter
  237. radius: sliderRoot.handleRadius
  238. color: lowerHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.lowerHandleColor
  239. visible: sliderRoot.layersVisible
  240. function onHandleDragged()
  241. {
  242. sliderRoot.manuallyChanged = true
  243. // don't allow the upper handle to be lower than the lower handle
  244. if (y - (upperHandle.y + upperHandle.height) < sliderRoot.minimumRangeHandleSize)
  245. {
  246. upperHandle.y = y - (upperHandle.height + sliderRoot.minimumRangeHandleSize)
  247. }
  248. // update the range handle
  249. sliderRoot.updateRangeHandle()
  250. // set the new value after moving the handle position
  251. UM.SimulationView.setMinimumLayer(getValue())
  252. }
  253. // get the lower value from the current slider position
  254. function getValue()
  255. {
  256. var result = (y - (sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize));
  257. result = sliderRoot.maximumValue - sliderRoot.minimumRange + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumRange))
  258. result = sliderRoot.roundValues ? Math.round(result) : result
  259. return result
  260. }
  261. function setValueManually(value)
  262. {
  263. sliderRoot.manuallyChanged = true
  264. lowerHandle.setValue(value)
  265. }
  266. // set the slider position based on the lower value
  267. function setValue(value)
  268. {
  269. // Normalize values between range, since using arrow keys will create out-of-the-range values
  270. value = sliderRoot.normalizeValue(value)
  271. UM.SimulationView.setMinimumLayer(value)
  272. var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
  273. // In case there is only one layer, the diff value results in a NaN, so this is for catching this specific case
  274. if (isNaN(diff))
  275. {
  276. diff = 0
  277. }
  278. var newLowerYPosition = Math.round((sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize) + diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
  279. y = newLowerYPosition
  280. // update the range handle
  281. sliderRoot.updateRangeHandle()
  282. }
  283. Keys.onUpPressed: lowerHandleLabel.setValue(lowerHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  284. Keys.onDownPressed: lowerHandleLabel.setValue(lowerHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  285. // dragging
  286. MouseArea
  287. {
  288. anchors.fill: parent
  289. drag
  290. {
  291. target: parent
  292. axis: Drag.YAxis
  293. minimumY: upperHandle.height + sliderRoot.minimumRangeHandleSize
  294. maximumY: sliderRoot.height - parent.height
  295. }
  296. onPositionChanged: parent.onHandleDragged()
  297. onPressed:
  298. {
  299. sliderRoot.setActiveHandle(lowerHandle)
  300. lowerHandleLabel.forceActiveFocus()
  301. }
  302. }
  303. SimulationSliderLabel
  304. {
  305. id: lowerHandleLabel
  306. height: sliderRoot.handleSize
  307. anchors.top: parent.bottom
  308. anchors.topMargin: UM.Theme.getSize("narrow_margin").height
  309. anchors.horizontalCenter: parent.horizontalCenter
  310. target: Qt.point(parent.width / 2, parent.bottom)
  311. visible: sliderRoot.activeHandle == parent || sliderRoot.activeHandle == rangeHandle
  312. // custom properties
  313. maximumValue: sliderRoot.maximumValue
  314. value: sliderRoot.lowerValue
  315. busy: UM.SimulationView.busy
  316. setValue: lowerHandle.setValueManually // connect callback functions
  317. }
  318. }
  319. }