LayerSlider.qml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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 1.2
  5. import QtQuick.Layouts 1.1
  6. import QtQuick.Controls.Styles 1.1
  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: sliderRoot.setActiveHandle(rangeHandle)
  130. }
  131. SimulationSliderLabel
  132. {
  133. id: rangleHandleLabel
  134. height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
  135. x: parent.x - width - UM.Theme.getSize("default_margin").width
  136. anchors.verticalCenter: parent.verticalCenter
  137. target: Qt.point(sliderRoot.width, y + height / 2)
  138. visible: sliderRoot.activeHandle == parent
  139. // custom properties
  140. maximumValue: sliderRoot.maximumValue
  141. value: sliderRoot.upperValue
  142. busy: UM.SimulationView.busy
  143. setValue: rangeHandle.setValueManually // connect callback functions
  144. }
  145. }
  146. onHeightChanged : {
  147. // After a height change, the pixel-position of the lower handle is out of sync with the property value
  148. setLowerValue(lowerValue)
  149. }
  150. // Upper handle
  151. Rectangle
  152. {
  153. id: upperHandle
  154. y: sliderRoot.height - (sliderRoot.minimumRangeHandleSize + 2 * sliderRoot.handleSize)
  155. width: sliderRoot.handleSize
  156. height: sliderRoot.handleSize
  157. anchors.horizontalCenter: sliderRoot.horizontalCenter
  158. radius: sliderRoot.handleRadius
  159. color: upperHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.upperHandleColor
  160. visible: sliderRoot.layersVisible
  161. function onHandleDragged()
  162. {
  163. sliderRoot.manuallyChanged = true
  164. // don't allow the lower handle to be heigher than the upper handle
  165. if (lowerHandle.y - (y + height) < sliderRoot.minimumRangeHandleSize)
  166. {
  167. lowerHandle.y = y + height + sliderRoot.minimumRangeHandleSize
  168. }
  169. // update the range handle
  170. sliderRoot.updateRangeHandle()
  171. // set the new value after moving the handle position
  172. UM.SimulationView.setCurrentLayer(getValue())
  173. }
  174. // get the upper value based on the slider position
  175. function getValue()
  176. {
  177. var result = y / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize))
  178. result = sliderRoot.maximumValue + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumValue))
  179. result = sliderRoot.roundValues ? Math.round(result) : result
  180. return result
  181. }
  182. function setValueManually(value)
  183. {
  184. sliderRoot.manuallyChanged = true
  185. upperHandle.setValue(value)
  186. }
  187. // set the slider position based on the upper value
  188. function setValue(value)
  189. {
  190. // Normalize values between range, since using arrow keys will create out-of-the-range values
  191. value = sliderRoot.normalizeValue(value)
  192. UM.SimulationView.setCurrentLayer(value)
  193. var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
  194. // In case there is only one layer, the diff value results in a NaN, so this is for catching this specific case
  195. if (isNaN(diff))
  196. {
  197. diff = 0
  198. }
  199. var newUpperYPosition = Math.round(diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
  200. y = newUpperYPosition
  201. // update the range handle
  202. sliderRoot.updateRangeHandle()
  203. }
  204. Keys.onUpPressed: upperHandleLabel.setValue(upperHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  205. Keys.onDownPressed: upperHandleLabel.setValue(upperHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  206. // dragging
  207. MouseArea
  208. {
  209. anchors.fill: parent
  210. drag
  211. {
  212. target: parent
  213. axis: Drag.YAxis
  214. minimumY: 0
  215. maximumY: sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)
  216. }
  217. onPositionChanged: parent.onHandleDragged()
  218. onPressed:
  219. {
  220. sliderRoot.setActiveHandle(upperHandle)
  221. upperHandleLabel.forceActiveFocus()
  222. }
  223. }
  224. SimulationSliderLabel
  225. {
  226. id: upperHandleLabel
  227. height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
  228. x: parent.x - parent.width - width
  229. anchors.verticalCenter: parent.verticalCenter
  230. target: Qt.point(sliderRoot.width, y + height / 2)
  231. visible: sliderRoot.activeHandle == parent
  232. // custom properties
  233. maximumValue: sliderRoot.maximumValue
  234. value: sliderRoot.upperValue
  235. busy: UM.SimulationView.busy
  236. setValue: upperHandle.setValueManually // connect callback functions
  237. }
  238. }
  239. // Lower handle
  240. Rectangle
  241. {
  242. id: lowerHandle
  243. y: sliderRoot.height - sliderRoot.handleSize
  244. width: parent.handleSize
  245. height: parent.handleSize
  246. anchors.horizontalCenter: parent.horizontalCenter
  247. radius: sliderRoot.handleRadius
  248. color: lowerHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.lowerHandleColor
  249. visible: sliderRoot.layersVisible
  250. function onHandleDragged()
  251. {
  252. sliderRoot.manuallyChanged = true
  253. // don't allow the upper handle to be lower than the lower handle
  254. if (y - (upperHandle.y + upperHandle.height) < sliderRoot.minimumRangeHandleSize)
  255. {
  256. upperHandle.y = y - (upperHandle.heigth + sliderRoot.minimumRangeHandleSize)
  257. }
  258. // update the range handle
  259. sliderRoot.updateRangeHandle()
  260. // set the new value after moving the handle position
  261. UM.SimulationView.setMinimumLayer(getValue())
  262. }
  263. // get the lower value from the current slider position
  264. function getValue()
  265. {
  266. var result = (y - (sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize));
  267. result = sliderRoot.maximumValue - sliderRoot.minimumRange + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumRange))
  268. result = sliderRoot.roundValues ? Math.round(result) : result
  269. return result
  270. }
  271. function setValueManually(value)
  272. {
  273. sliderRoot.manuallyChanged = true
  274. lowerHandle.setValue(value)
  275. }
  276. // set the slider position based on the lower value
  277. function setValue(value)
  278. {
  279. // Normalize values between range, since using arrow keys will create out-of-the-range values
  280. value = sliderRoot.normalizeValue(value)
  281. UM.SimulationView.setMinimumLayer(value)
  282. var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
  283. // In case there is only one layer, the diff value results in a NaN, so this is for catching this specific case
  284. if (isNaN(diff))
  285. {
  286. diff = 0
  287. }
  288. var newLowerYPosition = Math.round((sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize) + diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
  289. y = newLowerYPosition
  290. // update the range handle
  291. sliderRoot.updateRangeHandle()
  292. }
  293. Keys.onUpPressed: lowerHandleLabel.setValue(lowerHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  294. Keys.onDownPressed: lowerHandleLabel.setValue(lowerHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
  295. // dragging
  296. MouseArea
  297. {
  298. anchors.fill: parent
  299. drag
  300. {
  301. target: parent
  302. axis: Drag.YAxis
  303. minimumY: upperHandle.height + sliderRoot.minimumRangeHandleSize
  304. maximumY: sliderRoot.height - parent.height
  305. }
  306. onPositionChanged: parent.onHandleDragged()
  307. onPressed:
  308. {
  309. sliderRoot.setActiveHandle(lowerHandle)
  310. lowerHandleLabel.forceActiveFocus()
  311. }
  312. }
  313. SimulationSliderLabel
  314. {
  315. id: lowerHandleLabel
  316. height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
  317. x: parent.x - parent.width - width
  318. anchors.verticalCenter: parent.verticalCenter
  319. target: Qt.point(sliderRoot.width + width, y + height / 2)
  320. visible: sliderRoot.activeHandle == parent
  321. // custom properties
  322. maximumValue: sliderRoot.maximumValue
  323. value: sliderRoot.lowerValue
  324. busy: UM.SimulationView.busy
  325. setValue: lowerHandle.setValueManually // connect callback functions
  326. }
  327. }
  328. }