Skip to content

Commit

Permalink
Make SpinBox editable
Browse files Browse the repository at this point in the history
Upgrade the contentItem to use TextInput with correct VKB interaction.

Fixes #1104
  • Loading branch information
MikeTrahearn-Qinetic committed Jan 8, 2025
1 parent 517ead7 commit 6bd2bf9
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 14 deletions.
115 changes: 102 additions & 13 deletions components/controls/SpinBox.qml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import QtQuick
import QtQuick.Controls as C
import QtQuick.Templates as CT
import QtQuick.Controls.impl as CP
import QtQuick.Layouts
import Victron.VenusOS

// SpinBox uses a binding to increase 'stepSize' when the user holds a button down for a while. This allows the spin box to quickly change arbitrarily large values.
Expand All @@ -17,11 +18,12 @@ CT.SpinBox {
id: root

property alias label: primaryLabel
property string secondaryText
property alias secondaryText: secondaryLabel.text
property int indicatorImplicitWidth: Theme.geometry_spinBox_indicator_minimumWidth
property int orientation: Qt.Horizontal
property int _scalingFactor: 1
property int _originalStepSize
property alias suffix: suffixLabel.text

signal maxValueReached()
signal minValueReached()
Expand All @@ -45,29 +47,104 @@ CT.SpinBox {
}

contentItem: Item {

// needed for QQuickSpinBoxPrivate to read the "text" property of the contentItem
// so that it can call the valueFromText() function
readonly property alias text: primaryLabel.text

Column {
id: valueColumn

width: Math.max(primaryLabel.implicitWidth, secondaryLabel.implicitWidth)
width: Math.max(primaryTextInput.implicitWidth, secondaryLabel.implicitWidth)
anchors.centerIn: parent

Label {
id: primaryLabel

width: parent.width
text: root.textFromValue(root.value, root.locale)
color: root.enabled ? Theme.color_font_primary : Theme.color_background_disabled
font.pixelSize: root.secondaryText.length > 0 ? Theme.font_size_h2 : Theme.font_size_h3
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
Item {
id: primaryTextInput

width: primaryRowLayout.implicitWidth + Theme.geometry_textField_horizontalMargin * 2
height: primaryRowLayout.height
anchors.horizontalCenter: parent.horizontalCenter

MouseArea {
anchors.fill: parent
enabled: root.editable
onClicked: primaryLabel.forceActiveFocus()
}

Rectangle {
anchors.fill: parent
visible: root.editable
color: "transparent"
border.color: Theme.color_blue
border.width: Theme.geometry_button_border_width
radius: Theme.geometry_button_radius
}

RowLayout {
id: primaryRowLayout

anchors.centerIn: parent

TextInput {
id: primaryLabel

color: root.enabled ? Theme.color_font_primary : Theme.color_background_disabled
font.family: Global.fontFamily
font.pixelSize: root.secondaryText.length > 0 ? Theme.font_size_h2 : Theme.font_size_h3
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
selectedTextColor: Theme.color_white
selectionColor : Theme.color_blue
readOnly: !root.editable
selectByMouse: !readOnly
validator: root.validator
inputMethodHints: root.inputMethodHints

onAccepted: {
// Note that the text may at this time represent a value out of SpinBox
// to/from range, so clamp it here.
let v = root.valueFromText(text, root.locale)
if (v < root.from) {
v = root.from
} else if (v > root.to) {
v = root.to
}

// Force-update the displayed text, to guarantee the text is in sync
// with the numeric value, even if the value has not changed due to the
// user entering an out-of-range value on consecutive attempts.
text = root.textFromValue(v, root.locale)
root.value = v
primaryLabel.focus = false
}

Connections {
target: root
function onValueChanged() {
// Update the displayed text when the initial value is set or when
// the up/down buttons are pressed.
primaryLabel.text = root.textFromValue(root.value, root.locale)
}
}
}

Label {
id: suffixLabel

visible: text.length
color: primaryLabel.color
font: primaryLabel.font
horizontalAlignment: primaryLabel.horizontalAlignment
verticalAlignment: primaryLabel.verticalAlignment
}

}
}

Label {
id: secondaryLabel

width: primaryLabel.width
height: text.length ? implicitHeight : 0
text: root.secondaryText
color: Theme.color_font_secondary
font.pixelSize: Theme.font_size_caption
horizontalAlignment: Qt.AlignHCenter
Expand Down Expand Up @@ -116,6 +193,18 @@ CT.SpinBox {
}
}

textFromValue: function(value, locale) {
return Units.formatNumber(value)
}
valueFromText: function(text, locale) {
let value = Units.formattedNumberToReal(text)
if (isNaN(value)) {
// don't change the current value
value = root.value
}
return value
}

Timer {
id: pressTimer

Expand Down
12 changes: 11 additions & 1 deletion components/dialogs/NumberSelectorDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,25 @@ ModalDialog {
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 2*Theme.geometry_modalDialog_content_horizontalMargin
height: Theme.geometry_timeSelector_spinBox_height
editable: true
indicatorImplicitWidth: root.decimals > 0
? Theme.geometry_spinBox_indicator_minimumWidth
: Theme.geometry_spinBox_indicator_maximumWidth
textFromValue: function(value, locale) {
return Units.formatNumber(value / root._multiplier(), root.decimals) + root.suffix
return Units.formatNumber(value / root._multiplier(), root.decimals)
}
valueFromText: function(text, locale) {
let value = Units.formattedNumberToReal(text) * root._multiplier()
if (isNaN(value)) {
// don't change the current value
value = spinBox.value
}
return value
}
from: Math.max(Global.int32Min, root.from * root._multiplier())
to: Math.min(Global.int32Max, root.to * root._multiplier())
stepSize: root.stepSize * root._multiplier()
suffix: root.suffix

onValueChanged: {
if (_initialized) {
Expand Down

0 comments on commit 6bd2bf9

Please sign in to comment.