NumericTextFieldWithUnit.qml 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // Copyright (c) 2020 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.3
  5. import UM 1.5 as UM
  6. import Cura 1.1 as Cura
  7. //
  8. // TextField widget with validation for editing numeric data in the Machine Settings dialog.
  9. //
  10. UM.TooltipArea
  11. {
  12. id: numericTextFieldWithUnit
  13. UM.I18nCatalog { id: catalog; name: "cura"; }
  14. height: childrenRect.height
  15. width: childrenRect.width
  16. property int controlWidth: UM.Theme.getSize("setting_control").width
  17. property int controlHeight: UM.Theme.getSize("setting_control").height
  18. property real spacing: UM.Theme.getSize("default_margin").width
  19. text: tooltipText
  20. property alias containerStackId: propertyProvider.containerStackId
  21. property alias settingKey: propertyProvider.key
  22. property alias settingStoreIndex: propertyProvider.storeIndex
  23. property alias propertyProvider: propertyProvider
  24. property alias labelText: fieldLabel.text
  25. property alias labelFont: fieldLabel.font
  26. property alias labelWidth: fieldLabel.width
  27. property alias unitText: unitLabel.text
  28. property alias textField: textFieldWithUnit
  29. property alias valueText: textFieldWithUnit.text
  30. property alias enabled: textFieldWithUnit.enabled
  31. property alias editingFinishedFunction: textFieldWithUnit.editingFinishedFunction
  32. property string tooltipText: propertyProvider.properties.description ? propertyProvider.properties.description : ""
  33. property real minimum: 0
  34. property real maximum: Number.POSITIVE_INFINITY
  35. property int decimals: 6
  36. // callback functions
  37. property var afterOnEditingFinishedFunction: dummy_func
  38. property var forceUpdateOnChangeFunction: dummy_func
  39. property var setValueFunction: null
  40. // a dummy function for default property values
  41. function dummy_func() {}
  42. UM.SettingPropertyProvider
  43. {
  44. id: propertyProvider
  45. watchedProperties: [ "value", "description", "validationState" ]
  46. }
  47. UM.Label
  48. {
  49. id: fieldLabel
  50. anchors.left: parent.left
  51. anchors.verticalCenter: textFieldWithUnit.verticalCenter
  52. visible: text != ""
  53. }
  54. TextField
  55. {
  56. id: textFieldWithUnit
  57. anchors.left: fieldLabel.right
  58. anchors.leftMargin: spacing
  59. verticalAlignment: Text.AlignVCenter
  60. // The control is set up for left to right. So we force it to that. If we don't, it will take the OS reading
  61. // direction, which might not be left to right. This will lead to the text overlapping with the unit
  62. horizontalAlignment: TextInput.AlignLeft
  63. selectionColor: UM.Theme.getColor("text_selection")
  64. selectedTextColor: UM.Theme.getColor("setting_control_text")
  65. padding: 0
  66. leftPadding: UM.Theme.getSize("narrow_margin").width
  67. width: numericTextFieldWithUnit.controlWidth
  68. height: numericTextFieldWithUnit.controlHeight
  69. // Background is a rounded-cornered box with filled color as state indication (normal, warning, error, etc.)
  70. background: UM.UnderlineBackground
  71. {
  72. anchors.fill: parent
  73. borderColor: textFieldWithUnit.activeFocus ? UM.Theme.getColor("text_field_border_active") : "transparent"
  74. liningColor:
  75. {
  76. if (!textFieldWithUnit.enabled)
  77. {
  78. return UM.Theme.getColor("setting_control_disabled_border");
  79. }
  80. switch (propertyProvider.properties.validationState)
  81. {
  82. case "ValidatorState.Exception":
  83. case "ValidatorState.MinimumError":
  84. case "ValidatorState.MaximumError":
  85. return UM.Theme.getColor("setting_validation_error")
  86. case "ValidatorState.MinimumWarning":
  87. case "ValidatorState.MaximumWarning":
  88. return UM.Theme.getColor("setting_validation_warning")
  89. }
  90. // Validation is OK.
  91. if(textFieldWithUnit.activeFocus)
  92. {
  93. return UM.Theme.getColor("text_field_border_active");
  94. }
  95. if(textFieldWithUnit.hovered)
  96. {
  97. return UM.Theme.getColor("text_field_border_hovered");
  98. }
  99. return UM.Theme.getColor("border_field_light");
  100. }
  101. color:
  102. {
  103. if (!textFieldWithUnit.enabled)
  104. {
  105. return UM.Theme.getColor("setting_control_disabled")
  106. }
  107. switch (propertyProvider.properties.validationState)
  108. {
  109. case "ValidatorState.Exception":
  110. case "ValidatorState.MinimumError":
  111. case "ValidatorState.MaximumError":
  112. return UM.Theme.getColor("setting_validation_error_background")
  113. case "ValidatorState.MinimumWarning":
  114. case "ValidatorState.MaximumWarning":
  115. return UM.Theme.getColor("setting_validation_warning_background")
  116. case "ValidatorState.Valid":
  117. return UM.Theme.getColor("setting_validation_ok")
  118. default:
  119. return UM.Theme.getColor("setting_control")
  120. }
  121. }
  122. }
  123. hoverEnabled: true
  124. selectByMouse: true
  125. font: UM.Theme.getFont("default")
  126. color: UM.Theme.getColor("text")
  127. renderType: Text.NativeRendering
  128. // When the textbox gets focused by TAB, select all text
  129. onActiveFocusChanged:
  130. {
  131. if (activeFocus && (focusReason == Qt.TabFocusReason || focusReason == Qt.BacktabFocusReason))
  132. {
  133. selectAll()
  134. }
  135. }
  136. text:
  137. {
  138. const value = propertyProvider.properties.value
  139. return value ? value : ""
  140. }
  141. property string validatorString:
  142. {
  143. var digits = Math.min(8, 1 + Math.floor(
  144. Math.log(Math.max(Math.abs(numericTextFieldWithUnit.maximum), Math.abs(numericTextFieldWithUnit.minimum)))/Math.log(10)
  145. ))
  146. var minus = numericTextFieldWithUnit.minimum < 0 ? "-?" : ""
  147. if (numericTextFieldWithUnit.decimals == 0)
  148. {
  149. return "^%0\\d{1,%1}$".arg(minus).arg(digits)
  150. }
  151. else
  152. {
  153. return "^%0\\d{0,%1}[.,]?\\d{0,%2}$".arg(minus).arg(digits).arg(numericTextFieldWithUnit.decimals)
  154. }
  155. }
  156. validator: RegularExpressionValidator
  157. {
  158. regularExpression: new RegExp(textFieldWithUnit.validatorString)
  159. }
  160. //Enforce actual minimum and maximum values.
  161. //The DoubleValidator allows intermediate values, which essentially means that the maximum gets rounded up to the nearest power of 10.
  162. //This is not accurate at all, so here if the value exceeds the maximum or the minimum we disallow it.
  163. property string previousText
  164. onTextChanged:
  165. {
  166. var value = Number(text);
  167. if(value < numericTextFieldWithUnit.minimum || value > numericTextFieldWithUnit.maximum)
  168. {
  169. text = previousText;
  170. }
  171. previousText = text;
  172. }
  173. onEditingFinished: editingFinishedFunction()
  174. property var editingFinishedFunction: defaultEditingFinishedFunction
  175. function defaultEditingFinishedFunction()
  176. {
  177. if (propertyProvider && text != propertyProvider.properties.value)
  178. {
  179. // For some properties like the extruder-compatible material diameter, they need to
  180. // trigger many updates, such as the available materials, the current material may
  181. // need to be switched, etc. Although setting the diameter can be done directly via
  182. // the provider, all the updates that need to be triggered then need to depend on
  183. // the metadata update, a signal that can be fired way too often. The update functions
  184. // can have if-checks to filter out the irrelevant updates, but still it incurs unnecessary
  185. // overhead.
  186. // The ExtruderStack class has a dedicated function for this call "setCompatibleMaterialDiameter()",
  187. // and it triggers the diameter update signals only when it is needed. Here it is optionally
  188. // choose to use setCompatibleMaterialDiameter() or other more specific functions that
  189. // are available.
  190. if (setValueFunction !== null)
  191. {
  192. setValueFunction(text)
  193. }
  194. else
  195. {
  196. propertyProvider.setPropertyValue("value", text)
  197. }
  198. forceUpdateOnChangeFunction()
  199. afterOnEditingFinishedFunction()
  200. }
  201. }
  202. UM.Label
  203. {
  204. id: unitLabel
  205. anchors.right: parent.right
  206. anchors.rightMargin: Math.round(UM.Theme.getSize("setting_unit_margin").width)
  207. anchors.verticalCenter: parent.verticalCenter
  208. text: unitText
  209. textFormat: Text.PlainText
  210. color: UM.Theme.getColor("setting_unit")
  211. }
  212. }
  213. }