diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 0b15e726..71e73298 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -24,11 +24,57 @@ RowLayout { property alias minimum: root.from property alias maximum: root.to + // Properties for repeating + property int initialRepeatDelay: 400 // The "pause" after the first click (in ms) + property int repeatInterval: 80 // How often to step up after fist pause (ms) + property int rampFactor: 4 // How many ticks to wait before increasing the step multiplier + property int maxStepMultiplier: 10 // The max step (e.g., 10 * stepSize) + property int _holdTicks: 0 // Internal counter for hold duration + property int _repeatDirection: 0 // -1 for decrease, 1 for increase + signal entered signal exited Layout.fillWidth: true + Timer { + id: repeatTimer + repeat: true + interval: root.initialRepeatDelay + + onTriggered: { + if (repeatTimer.interval === root.initialRepeatDelay) { + repeatTimer.interval = root.repeatInterval; + root._holdTicks = 0; + } + root._holdTicks++; + var stepMultiplier = Math.min(root.maxStepMultiplier, 1 + Math.floor(root._holdTicks / root.rampFactor)); + changeValue(root._repeatDirection, root.stepSize * stepMultiplier); + } + } + + function changeValue(direction, step) { + var currentStep = step || root.stepSize; + + if (direction === 1 && root.value < root.to) { + root.value = Math.min(root.to, root.value + currentStep); + } else if (direction === -1 && root.value > root.from) { + root.value = Math.max(root.from, root.value - currentStep); + } else { + return; + } + + if (root.value === root.to || root.value === root.from) { + stopRepeat(); + } + } + + function stopRepeat() { + root._repeatDirection = 0; + repeatTimer.stop(); + repeatTimer.interval = root.initialRepeatDelay; + } + NLabel { label: root.label description: root.description @@ -38,7 +84,7 @@ RowLayout { Rectangle { id: spinBoxContainer implicitWidth: 120 - implicitHeight: (root.baseSize - 4) + implicitHeight: Math.round((root.baseSize - 4)/2)*2 radius: height * 0.5 color: Color.mSurfaceVariant border.color: (root.hovering || decreaseArea.containsMouse || increaseArea.containsMouse) ? Color.mHover : Color.mOutline @@ -82,7 +128,8 @@ RowLayout { anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: parent.left - opacity: root.enabled && root.value > root.from ? 1.0 : 0.3 + opacity: (root.enabled && root.value > root.from) || decreaseArea.containsMouse ? 1.0 : 0.3 + clip: true Item { id: leftSemicircle @@ -93,7 +140,7 @@ RowLayout { Rectangle { width: Math.round(parent.height) height: parent.height - radius: width / 2 + radius: Math.round(width / 2) anchors.left: parent.left color: decreaseArea.containsMouse ? Color.mHover : Color.transparent Behavior on color { @@ -162,10 +209,13 @@ RowLayout { hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: root.enabled && root.value > root.from - onClicked: { - let newValue = Math.max(root.from, root.value - root.stepSize); - root.value = newValue; + onPressed: { + root._repeatDirection = -1; + changeValue(root._repeatDirection, root.stepSize); + repeatTimer.start(); } + onReleased: stopRepeat() + onExited: stopRepeat() } } @@ -177,7 +227,8 @@ RowLayout { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right - opacity: root.enabled && root.value < root.to ? 1.0 : 0.3 + opacity: (root.enabled && root.value < root.to) || increaseArea.containsMouse ? 1.0 : 0.3 + clip: true Item { id: rightSemicircle @@ -188,7 +239,7 @@ RowLayout { Rectangle { width: Math.round(parent.height) height: parent.height - radius: width / 2 + radius: Math.round(width / 2) anchors.right: parent.right color: increaseArea.containsMouse ? Color.mHover : Color.transparent Behavior on color { @@ -257,10 +308,13 @@ RowLayout { hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: root.enabled && root.value < root.to - onClicked: { - let newValue = Math.min(root.to, root.value + root.stepSize); - root.value = newValue; + onPressed: { + root._repeatDirection = 1; + changeValue(root._repeatDirection, root.stepSize); + repeatTimer.start(); } + onReleased: stopRepeat() + onExited: stopRepeat() } }