LayerSlider.qml 13 KB

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