From 0d2d0f1931f5b9afb0b1e4722b0b16fcd7a1d9cd Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 28 Sep 2025 12:49:52 -0400 Subject: [PATCH 1/4] Nicer SpinBox with better mTertiary hover --- Widgets/NSpinBox.qml | 160 ++++++++++++++++++++++++++++++------------- 1 file changed, 114 insertions(+), 46 deletions(-) diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 0f5feb0f..3d5c0e5e 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -5,6 +5,7 @@ import qs.Commons import qs.Services import qs.Widgets + RowLayout { id: root @@ -38,12 +39,11 @@ RowLayout { // Value Rectangle { id: spinBoxContainer - implicitWidth: 100 * scaling // Wider for better proportions implicitHeight: (root.baseSize - 4) * scaling // Slightly shorter than toggle - radius: height * 0.5 // Fully rounded like toggle + radius: height / 2 // Fully rounded like toggle color: Color.mSurfaceVariant - border.color: root.hovering ? Color.mPrimary : Color.mOutline + border.color: (root.hovering || decreaseArea.containsMouse || increaseArea.containsMouse) ? Color.mTertiary : Color.mOutline border.width: 1 Behavior on border.color { @@ -75,56 +75,124 @@ RowLayout { } // Decrease button (left) - Rectangle { + Item { id: decreaseButton - width: parent.height * 0.8 // Make it circular - height: parent.height * 0.8 - anchors.verticalCenter: parent.verticalCenter + height:parent.height + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom anchors.left: parent.left - anchors.leftMargin: parent.height * 0.1 - radius: width * 0.5 // Perfect circle - color: decreaseArea.containsMouse ? Color.mPrimary : "transparent" opacity: root.enabled && spinBox.value > spinBox.from ? 1.0 : 0.3 - Behavior on color { - ColorAnimation { - duration: Style.animationFast - easing.type: Easing.InOutCubic + Item { + id: leftSemicircle + width: Math.round(parent.height / 2) + height: parent.height + clip: true + anchors.left: parent.left + Rectangle { + width: Math.round(parent.height) + height: parent.height + radius: width / 2 + anchors.left: parent.left + color: decreaseArea.containsMouse ? Color.mTertiary : "transparent" + Behavior on color { + ColorAnimation { + duration: Style.animationFast + } + } + } + } + + Rectangle { + height: parent.height + width: parent.width - leftSemicircle.width + anchors.left: leftSemicircle.right + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { + position: 0.0 + color: decreaseArea.containsMouse ? Color.mTertiary : "transparent" + Behavior on color { + ColorAnimation { + duration: Style.animationFast + } + } + } + GradientStop { + position: 1.0 + color: "transparent" + } + } + } + + NIcon { + anchors.centerIn: parent + icon: "chevron-left" + font.pointSize: Style.fontSizeS * scaling + color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary + } + + MouseArea { + id: decreaseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + enabled: root.enabled && spinBox.value > spinBox.from + onClicked: spinBox.decrease() + } + } + + + // Increase button (right) + Item { + id: increaseButton + height: parent.height + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + opacity: root.enabled && spinBox.value < spinBox.to ? 1.0 : 0.3 + + Item { + id: rightSemicircle + width: Math.round(parent.height / 2) + height: parent.height + clip: true + anchors.right: parent.right + Rectangle { + width: Math.round(parent.height) + height: parent.height + radius: width / 2 + anchors.right: parent.right + color: increaseArea.containsMouse ? Color.mTertiary : "transparent" + Behavior on color { + ColorAnimation { + duration: Style.animationFast + } + } } } - NIcon { - anchors.centerIn: parent - icon: "chevron-left" - font.pointSize: Style.fontSizeS * scaling - color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary - } - - MouseArea { - id: decreaseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - enabled: root.enabled && spinBox.value > spinBox.from - onClicked: spinBox.decrease() - } - } - - // Increase button (right) - Rectangle { - id: increaseButton - width: parent.height * 0.8 // Make it circular - height: parent.height * 0.8 - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: parent.height * 0.1 - radius: width * 0.5 // Perfect circle - color: increaseArea.containsMouse ? Color.mPrimary : "transparent" - opacity: root.enabled && spinBox.value < spinBox.to ? 1.0 : 0.3 - - Behavior on color { - ColorAnimation { - duration: Style.animationFast + Rectangle { + height: parent.height + width: parent.width - rightSemicircle.width + anchors.right: rightSemicircle.left + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { + position: 1.0 + color: increaseArea.containsMouse ? Color.mTertiary : "transparent" + Behavior on color { + ColorAnimation { + duration: Style.animationFast + } + } + } + GradientStop { + position: 0.0 + color: "transparent" + } } } From 5f3c088f2201700870c963e21b18dea7fec41689 Mon Sep 17 00:00:00 2001 From: MrDowntempo Date: Sun, 28 Sep 2025 13:16:07 -0400 Subject: [PATCH 2/4] Update NSpinBox.qml I missed a line --- Widgets/NSpinBox.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 686e9c82..8562da1b 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -158,7 +158,6 @@ RowLayout { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right - opacity: root.enabled && spinBox.value < spinBox.to ? 1.0 : 0.3 opacity: root.enabled && root.value < root.to ? 1.0 : 0.3 From a41be0b5d99227bc3d6a13336e58632747a932b3 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 28 Sep 2025 19:08:33 -0400 Subject: [PATCH 3/4] Removed gradient and redesigned buttons --- Widgets/NSpinBox.qml | 151 +++++++++++++++++++++++++++++++------------ 1 file changed, 111 insertions(+), 40 deletions(-) diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 8562da1b..d1ee2ed1 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -80,12 +80,12 @@ RowLayout { Item { id: decreaseButton height:parent.height - width: height + width: leftSemicircle.width + (leftDiamondContainer.width / 2) anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: parent.left opacity: root.enabled && root.value > root.from ? 1.0 : 0.3 - + clip: true Item { id: leftSemicircle @@ -107,31 +107,59 @@ RowLayout { } } - Rectangle { - height: parent.height - width: parent.width - leftSemicircle.width - anchors.left: leftSemicircle.right - gradient: Gradient { - orientation: Gradient.Horizontal - GradientStop { - position: 0.0 - color: decreaseArea.containsMouse ? Color.mTertiary : "transparent" - Behavior on color { - ColorAnimation { - duration: Style.animationFast - } + // 1. A wrapper Item to define the final, pixel-perfect size. + Item { + id: leftDiamondContainer + + // Make the container's height a guaranteed even number + height: Math.round(parent.height / 2) * 2 + width: height * Math.sqrt(2) // Keep the final bounding box square + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: leftSemicircle.right + + // 2. The Rectangle to be rotated and scaled. + // We give it a fixed base size (e.g., 100x100) to make calculations stable. + Rectangle { + id: leftDiamondVisual + width: 100 + height: 100 + + // The radius is now based on the fixed size + radius: width / 4 + + color: decreaseArea.containsMouse ? Color.mTertiary : "transparent" + Behavior on color { + ColorAnimation { + duration: Style.animationFast } } - GradientStop { - position: 1.0 - color: "transparent" - } + + // Center the base shape in the container + anchors.centerIn: parent + + // 3. Apply transforms: first rotate, then scale to fit. + transform: [ + Rotation { angle: 45; origin.x: 50; origin.y: 50 }, + Scale { + id: leftScaler + origin.x: 50 + origin.y: 50 + + // This is the full formula for the height of the rotated, rounded square + readonly property real trueHeight: (leftDiamondVisual.width - 2 * leftDiamondVisual.radius) * Math.sqrt(2) + (2 * leftDiamondVisual.radius) + + // The corrected scale factor + xScale: leftDiamondContainer.height / leftScaler.trueHeight + yScale: leftDiamondContainer.height / leftScaler.trueHeight + } + ] } } + NIcon { anchors.centerIn: parent - icon: "chevron-left" + icon: "minus" font.pointSize: Style.fontSizeS * scaling color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary } @@ -154,12 +182,12 @@ RowLayout { Item { id: increaseButton height: parent.height - width: height + width: rightSemicircle.width + (rightDiamondContainer.width / 2) anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right opacity: root.enabled && root.value < root.to ? 1.0 : 0.3 - + clip: true Item { id: rightSemicircle @@ -181,31 +209,74 @@ RowLayout { } } - Rectangle { - height: parent.height - width: parent.width - rightSemicircle.width - anchors.right: rightSemicircle.left - gradient: Gradient { - orientation: Gradient.Horizontal - GradientStop { - position: 1.0 - color: increaseArea.containsMouse ? Color.mTertiary : "transparent" - Behavior on color { - ColorAnimation { - duration: Style.animationFast - } + Item { + id: rightDiamondContainer + + // Make the container's height a guaranteed even number + height: Math.round(parent.height / 2) * 2 + width: height * Math.sqrt(2) // Keep the final bounding box square + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: rightSemicircle.left + + // 2. The Rectangle to be rotated and scaled. + // We give it a fixed base size (e.g., 100x100) to make calculations stable. + Rectangle { + id: rightDiamondVisual + width: 100 + height: 100 + + // The radius is now based on the fixed size + radius: width / 4 + + color: increaseArea.containsMouse ? Color.mTertiary : "transparent" + Behavior on color { + ColorAnimation { + duration: Style.animationFast } } - GradientStop { - position: 0.0 - color: "transparent" - } + + // Center the base shape in the container + anchors.centerIn: parent + + // 3. Apply transforms: first rotate, then scale to fit. + transform: [ + Rotation { angle: 45; origin.x: 50; origin.y: 50 }, + Scale { + id: rightScaler + origin.x: 50 + origin.y: 50 + + // This is the full formula for the height of the rotated, rounded square + readonly property real trueHeight: (rightDiamondVisual.width - 2 * rightDiamondVisual.radius) * Math.sqrt(2) + (2 * rightDiamondVisual.radius) + + // The corrected scale factor + xScale: rightDiamondContainer.height / rightScaler.trueHeight + yScale: rightDiamondContainer.height / rightScaler.trueHeight + } + ] } } + // + // Rectangle { + // height: (parent.height / Math.sqrt(2)) + (radius * (2 - Math.sqrt(2))) + // width: height + // radius: width / 4 + // anchors.verticalCenter: parent.verticalCenter + // anchors.horizontalCenter: rightSemicircle.left + // rotation: 45 + // color: increaseArea.containsMouse ? Color.mTertiary : "transparent" + // Behavior on color { + // ColorAnimation { + // duration: Style.animationFast + // } + // } + // } + + NIcon { anchors.centerIn: parent - icon: "chevron-right" + icon: "plus" font.pointSize: Style.fontSizeS * scaling color: increaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary } From 97fa2fb1b55fbefb5515ecde398e80401987eab9 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 28 Sep 2025 20:20:02 -0400 Subject: [PATCH 4/4] Back to Chevrons. +/- were inconsistent sizes. Better alignment --- Widgets/NSpinBox.qml | 54 ++++++++------------------------------------ 1 file changed, 10 insertions(+), 44 deletions(-) diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index d1ee2ed1..05546980 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -107,24 +107,18 @@ RowLayout { } } - // 1. A wrapper Item to define the final, pixel-perfect size. Item { id: leftDiamondContainer - // Make the container's height a guaranteed even number height: Math.round(parent.height / 2) * 2 - width: height * Math.sqrt(2) // Keep the final bounding box square + width: height * Math.sqrt(2) anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: leftSemicircle.right - // 2. The Rectangle to be rotated and scaled. - // We give it a fixed base size (e.g., 100x100) to make calculations stable. Rectangle { id: leftDiamondVisual width: 100 height: 100 - - // The radius is now based on the fixed size radius: width / 4 color: decreaseArea.containsMouse ? Color.mTertiary : "transparent" @@ -134,10 +128,8 @@ RowLayout { } } - // Center the base shape in the container anchors.centerIn: parent - // 3. Apply transforms: first rotate, then scale to fit. transform: [ Rotation { angle: 45; origin.x: 50; origin.y: 50 }, Scale { @@ -147,8 +139,6 @@ RowLayout { // This is the full formula for the height of the rotated, rounded square readonly property real trueHeight: (leftDiamondVisual.width - 2 * leftDiamondVisual.radius) * Math.sqrt(2) + (2 * leftDiamondVisual.radius) - - // The corrected scale factor xScale: leftDiamondContainer.height / leftScaler.trueHeight yScale: leftDiamondContainer.height / leftScaler.trueHeight } @@ -156,10 +146,11 @@ RowLayout { } } - NIcon { - anchors.centerIn: parent - icon: "minus" + anchors.left: parent.left + anchors.leftMargin: parent.width * 0.25 + anchors.verticalCenter: parent.verticalCenter + icon: "chevron-left" font.pointSize: Style.fontSizeS * scaling color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary } @@ -177,7 +168,6 @@ RowLayout { } } - // Increase button (right) Item { id: increaseButton @@ -212,20 +202,15 @@ RowLayout { Item { id: rightDiamondContainer - // Make the container's height a guaranteed even number height: Math.round(parent.height / 2) * 2 - width: height * Math.sqrt(2) // Keep the final bounding box square + width: height * Math.sqrt(2) anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: rightSemicircle.left - // 2. The Rectangle to be rotated and scaled. - // We give it a fixed base size (e.g., 100x100) to make calculations stable. Rectangle { id: rightDiamondVisual width: 100 height: 100 - - // The radius is now based on the fixed size radius: width / 4 color: increaseArea.containsMouse ? Color.mTertiary : "transparent" @@ -235,10 +220,8 @@ RowLayout { } } - // Center the base shape in the container anchors.centerIn: parent - // 3. Apply transforms: first rotate, then scale to fit. transform: [ Rotation { angle: 45; origin.x: 50; origin.y: 50 }, Scale { @@ -248,8 +231,6 @@ RowLayout { // This is the full formula for the height of the rotated, rounded square readonly property real trueHeight: (rightDiamondVisual.width - 2 * rightDiamondVisual.radius) * Math.sqrt(2) + (2 * rightDiamondVisual.radius) - - // The corrected scale factor xScale: rightDiamondContainer.height / rightScaler.trueHeight yScale: rightDiamondContainer.height / rightScaler.trueHeight } @@ -257,26 +238,11 @@ RowLayout { } } - // - // Rectangle { - // height: (parent.height / Math.sqrt(2)) + (radius * (2 - Math.sqrt(2))) - // width: height - // radius: width / 4 - // anchors.verticalCenter: parent.verticalCenter - // anchors.horizontalCenter: rightSemicircle.left - // rotation: 45 - // color: increaseArea.containsMouse ? Color.mTertiary : "transparent" - // Behavior on color { - // ColorAnimation { - // duration: Style.animationFast - // } - // } - // } - - NIcon { - anchors.centerIn: parent - icon: "plus" + anchors.right: parent.right + anchors.rightMargin: parent.width * 0.25 + anchors.verticalCenter: parent.verticalCenter + icon: "chevron-right" font.pointSize: Style.fontSizeS * scaling color: increaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary }