From 4da1b08e19d48a714390eb3f25c966ebf35a980d Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Mon, 17 Nov 2025 12:57:42 -0500 Subject: [PATCH 1/2] Fix vertical centering and overflow of NSpinBox --- Widgets/NSpinBox.qml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 0b15e726..9d5abfe0 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -38,7 +38,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 @@ -83,6 +83,7 @@ RowLayout { anchors.bottom: parent.bottom anchors.left: parent.left opacity: root.enabled && root.value > root.from ? 1.0 : 0.3 + clip: true Item { id: leftSemicircle @@ -93,7 +94,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 { @@ -178,6 +179,7 @@ RowLayout { anchors.bottom: parent.bottom anchors.right: parent.right opacity: root.enabled && root.value < root.to ? 1.0 : 0.3 + clip: true Item { id: rightSemicircle @@ -188,7 +190,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 { From 8bca19f3f0ebd21a544274a871aaea9c74d9ab53 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Mon, 17 Nov 2025 15:26:21 -0500 Subject: [PATCH 2/2] Add the ability to click and hold to speed up adjustments --- Widgets/NSpinBox.qml | 68 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 9d5abfe0..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 @@ -82,7 +128,7 @@ 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 { @@ -163,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() } } @@ -178,7 +227,7 @@ 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 { @@ -259,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() } }