SpinBox.qml 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright (c) 2022 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.15
  4. import QtQuick.Controls 2.15
  5. import UM 1.5 as UM
  6. import Cura 1.5 as Cura
  7. // This component extends the funtionality of QtControls 2.x Spinboxes to
  8. // - be able to contain fractional values
  9. // - hava a "prefix" and a "suffix". A validator is added that recognizes this pre-, suf-fix combo. When adding a custom
  10. // validator the pre-, suf-fix should be added (e.g. new RegExp("^" + prefix + \regex\ + suffix + "$")
  11. Item
  12. {
  13. id: base
  14. property string prefix: ""
  15. property string suffix: ""
  16. property int decimals: 0
  17. property real stepSize: 1
  18. property real value: 0
  19. property real from: 0
  20. property real to: 99
  21. property alias wrap: spinBox.wrap
  22. property bool editable: true
  23. property var validator: RegularExpressionValidator
  24. {
  25. regularExpression: new RegExp("^" + prefix + "([0-9]+[.|,]?[0-9]*)?" + suffix + "$")
  26. }
  27. signal editingFinished()
  28. height: UM.Theme.getSize("setting_control").height
  29. SpinBox
  30. {
  31. id: spinBox
  32. anchors.fill: base
  33. editable: base.editable
  34. topPadding: 0
  35. bottomPadding: 0
  36. leftPadding: down.indicator.width + UM.Theme.getSize("narrow_margin").width
  37. rightPadding: up.indicator.width + UM.Theme.getSize("narrow_margin").width
  38. // The stepSize of the SpinBox is intentionally set to be always `1`
  39. // As SpinBoxes can only contain integer values the `base.stepSize` is concidered the precision/resolution
  40. // increasing the spinBox.value by one increases the actual/real value of the component by `base.stepSize`
  41. // as such spinBox.value * base.stepSizes produces the real value of the component
  42. stepSize: 1
  43. value: Math.floor(base.value / base.stepSize)
  44. from: Math.floor(base.from / base.stepSize)
  45. to: Math.floor(base.to / base.stepSize)
  46. function clamp(value, min, max)
  47. {
  48. return Math.max((Math.min(value, max)), min);
  49. }
  50. valueFromText: function(text)
  51. {
  52. var value = parseFloat(text.substring(prefix.length, text.length - suffix.length).replace(",", ".")) / base.stepSize;
  53. if (Number.isNaN(value))
  54. {
  55. value = from
  56. }
  57. return clamp(value, from, to);
  58. }
  59. textFromValue: function(value)
  60. {
  61. return prefix + (value * base.stepSize).toFixed(decimals) + suffix;
  62. }
  63. validator: base.validator
  64. onValueModified:
  65. {
  66. base.value = value * base.stepSize;
  67. spinBoxText.text = spinBox.textFromValue(value);
  68. }
  69. // This forces TextField to commit typed values before incrementing with buttons.
  70. // This fixes the typed value not being incremented when the textField has active focus.
  71. up.onPressedChanged:
  72. {
  73. base.forceActiveFocus()
  74. }
  75. down.onPressedChanged:
  76. {
  77. base.forceActiveFocus()
  78. }
  79. background: Item {}
  80. contentItem: Cura.TextField
  81. {
  82. id: spinBoxText
  83. text: spinBox.textFromValue(spinBox.value, spinBox.locale)
  84. validator: base.validator
  85. onActiveFocusChanged:
  86. {
  87. if (!activeFocus)
  88. {
  89. base.editingFinished();
  90. }
  91. }
  92. onTextChanged:
  93. {
  94. var value = spinBox.valueFromText(text);
  95. text = spinBox.textFromValue(value);
  96. base.value = value;
  97. }
  98. }
  99. down.indicator: Rectangle
  100. {
  101. x: spinBox.mirrored ? parent.width - width : 0
  102. height: parent.height
  103. width: height
  104. UM.UnderlineBackground {
  105. color: spinBox.down.pressed ? spinBox.palette.mid : UM.Theme.getColor("detail_background")
  106. }
  107. UM.ColorImage
  108. {
  109. anchors.centerIn: parent
  110. height: parent.height / 2.5
  111. width: height
  112. color: enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("text_disabled")
  113. source: UM.Theme.getIcon("Minus")
  114. }
  115. }
  116. up.indicator: Rectangle
  117. {
  118. x: spinBox.mirrored ? 0 : parent.width - width
  119. height: parent.height
  120. width: height
  121. UM.UnderlineBackground {
  122. color: spinBox.up.pressed ? spinBox.palette.mid : UM.Theme.getColor("detail_background")
  123. }
  124. UM.ColorImage
  125. {
  126. anchors.centerIn: parent
  127. height: parent.height / 2.5
  128. width: height
  129. color: enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("text_disabled")
  130. source: UM.Theme.getIcon("Plus")
  131. }
  132. }
  133. }
  134. }