LayerSlider.qml 13 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: 10
  14. property real handleRadius: handleSize / 2
  15. property real minimumRangeHandleSize: handleSize / 2
  16. property color upperHandleColor: "black"
  17. property color lowerHandleColor: "black"
  18. property color rangeHandleColor: "black"
  19. property color handleActiveColor: "white"
  20. property real handleLabelWidth: width
  21. property var activeHandle: upperHandle
  22. // track properties
  23. property real trackThickness: 4 // width of the slider track
  24. property real trackRadius: trackThickness / 2
  25. property color trackColor: "white"
  26. property real trackBorderWidth: 1 // width of the slider track border
  27. property color trackBorderColor: "black"
  28. // value properties
  29. property real maximumValue: 100
  30. property real minimumValue: 0
  31. property real minimumRange: 0 // minimum range allowed between min and max values
  32. property bool roundValues: true
  33. property real upperValue: maximumValue
  34. property real lowerValue: minimumValue
  35. property bool layersVisible: true
  36. property bool manuallyChanged: true // Indicates whether the value was changed manually or during simulation
  37. function getUpperValueFromSliderHandle()
  38. {
  39. return upperHandle.getValue()
  40. }
  41. function setUpperValue(value)
  42. {
  43. upperHandle.setValue(value)
  44. updateRangeHandle()
  45. }
  46. function getLowerValueFromSliderHandle()
  47. {
  48. return lowerHandle.getValue()
  49. }
  50. function setLowerValue(value)
  51. {
  52. lowerHandle.setValue(value)
  53. updateRangeHandle()
  54. }
  55. function updateRangeHandle()
  56. {
  57. rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
  58. }
  59. // set the active handle to show only one label at a time
  60. function setActiveHandle(handle)
  61. {
  62. activeHandle = handle
  63. }
  64. function normalizeValue(value)
  65. {
  66. return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
  67. }
  68. // slider track
  69. Rectangle
  70. {
  71. id: track
  72. width: sliderRoot.trackThickness
  73. height: sliderRoot.height - sliderRoot.handleSize
  74. radius: sliderRoot.trackRadius
  75. anchors.centerIn: sliderRoot
  76. color: sliderRoot.trackColor
  77. border.width: sliderRoot.trackBorderWidth
  78. border.color: sliderRoot.trackBorderColor
  79. visible: sliderRoot.layersVisible
  80. }
  81. // Range handle
  82. Item
  83. {
  84. id: rangeHandle
  85. y: upperHandle.y + upperHandle.height
  86. width: sliderRoot.handleSize
  87. height: sliderRoot.minimumRangeHandleSize
  88. anchors.horizontalCenter: sliderRoot.horizontalCenter
  89. visible: sliderRoot.layersVisible
  90. // set the new value when dragging
  91. function onHandleDragged()
  92. {
  93. sliderRoot.manuallyChanged = true
  94. upperHandle.y = y - upperHandle.height
  95. lowerHandle.y = y + height
  96. var upperValue = sliderRoot.getUpperValueFromSliderHandle()
  97. var lowerValue = sliderRoot.getLowerValueFromSliderHandle()
  98. // set both values after moving the handle position
  99. UM.SimulationView.setCurrentLayer(upperValue)
  100. UM.SimulationView.setMinimumLayer(lowerValue)
  101. }
  102. function setValueManually(value)
  103. {
  104. sliderRoot.manuallyChanged = true
  105. upperHandle.setValue(value)
  106. }
  107. function setValue(value)
  108. {
  109. var range = sliderRoot.upperValue - sliderRoot.lowerValue
  110. value = Math.min(value, sliderRoot.maximumValue)
  111. value = Math.max(value, sliderRoot.minimumValue + range)
  112. UM.SimulationView.setCurrentLayer(value)
  113. UM.SimulationView.setMinimumLayer(value - range)
  114. }
  115. Rectangle
  116. {
  117. width: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth
  118. height: parent.height + sliderRoot.handleSize
  119. anchors.centerIn: parent
  120. color: sliderRoot.rangeHandleColor
  121. }
  122. MouseArea
  123. {
  124. anchors.fill: parent
  125. drag
  126. {
  127. target: parent
  128. axis: Drag.YAxis
  129. minimumY: upperHandle.height
  130. maximumY: sliderRoot.height - (rangeHandle.height + lowerHandle.height)
  131. }
  132. onPositionChanged: parent.onHandleDragged()
  133. onPressed: sliderRoot.setActiveHandle(rangeHandle)
  134. }
  135. SimulationSliderLabel
  136. {
  137. id: rangleHandleLabel
  138. height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
  139. x: parent.x - width - UM.Theme.getSize("default_margin").width
  140. anchors.verticalCenter: parent.verticalCenter
  141. target: Qt.point(sliderRoot.width, y + height / 2)
  142. visible: sliderRoot.activeHandle == parent
  143. // custom properties
  144. maximumValue: sliderRoot.maximumValue
  145. value: sliderRoot.upperValue
  146. busy: UM.SimulationView.busy
  147. setValue: rangeHandle.setValueManually // connect callback functions
  148. }
  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 - width - UM.Theme.getSize("default_margin").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 - width - UM.Theme.getSize("default_margin").width
  318. anchors.verticalCenter: parent.verticalCenter
  319. target: Qt.point(sliderRoot.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. }