NValueSlider: new component + pimped NSlider with a small gradient and removed rounded corners due to issues.

This commit is contained in:
LemmyCook 2025-09-14 20:52:32 -04:00
parent 5ce5659b38
commit 5aa7ff7e91
14 changed files with 266 additions and 441 deletions

View file

@ -243,24 +243,19 @@ Popup {
Layout.preferredWidth: 20 * scaling
}
NSlider {
NValueSlider {
id: redSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.r * 255)
onMoved: {
root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1)
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
}
NText {
text: Math.round(redSlider.value)
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 30 * scaling
onMoved: value => {
root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1)
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
text: Math.round(value)
}
}
@ -274,25 +269,20 @@ Popup {
Layout.preferredWidth: 20 * scaling
}
NSlider {
NValueSlider {
id: greenSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.g * 255)
onMoved: {
root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
}
NText {
text: Math.round(greenSlider.value)
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 30 * scaling
onMoved: value => {
root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
text: Math.round(value)
}
}
@ -306,25 +296,20 @@ Popup {
Layout.preferredWidth: 20 * scaling
}
NSlider {
NValueSlider {
id: blueSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.b * 255)
onMoved: {
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
}
NText {
text: Math.round(blueSlider.value)
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 30 * scaling
onMoved: value => {
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
text: Math.round(value)
}
}
@ -338,7 +323,7 @@ Popup {
Layout.preferredWidth: 80 * scaling
}
NSlider {
NValueSlider {
id: brightnessSlider
Layout.fillWidth: true
from: 0
@ -347,27 +332,22 @@ Popup {
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
return hsv[2]
}
onMoved: {
var hue = root.currentHue
var saturation = root.currentSaturation
onMoved: value => {
var hue = root.currentHue
var saturation = root.currentSaturation
if (hue === 0 && saturation === 0) {
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
hue = hsv[0]
saturation = hsv[1]
root.currentHue = hue
root.currentSaturation = saturation
}
if (hue === 0 && saturation === 0) {
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
hue = hsv[0]
saturation = hsv[1]
root.currentHue = hue
root.currentSaturation = saturation
}
var rgb = root.hsvToRgb(hue, saturation, value)
root.selectedColor = Qt.rgba(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 1)
}
}
NText {
var rgb = root.hsvToRgb(hue, saturation, value)
root.selectedColor = Qt.rgba(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 1)
}
text: Math.round(brightnessSlider.value) + "%"
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 40 * scaling
}
}
}

View file

@ -7,13 +7,12 @@ import qs.Services
Slider {
id: root
// Optional color to cut the track beneath the knob (should match surrounding background)
property var cutoutColor
property var cutoutColor: Color.mSurface
property bool snapAlways: true
property real heightRatio: 0.75
readonly property real knobDiameter: Math.round(Style.baseWidgetSize * heightRatio * scaling)
readonly property real trackHeight: knobDiameter * 0.5
readonly property real trackHeight: knobDiameter * 0.4
readonly property real cutoutExtra: Math.round(Style.baseWidgetSize * 0.1 * scaling)
snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease
@ -26,15 +25,38 @@ Slider {
implicitHeight: trackHeight
width: root.availableWidth
height: implicitHeight
radius: height / 2
radius: 0
color: Color.mSurface
// Animated gradient active track
Rectangle {
id: activeTrack
width: root.visualPosition * parent.width
height: parent.height
color: Color.mPrimary
radius: parent.radius
// Animated gradient fill
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
position: 0.0
color: Qt.darker(Color.mPrimary, 1.2)
Behavior on color { ColorAnimation { duration: 300 } }
}
GradientStop {
position: 0.5
color: Color.mPrimary
SequentialAnimation on position {
loops: Animation.Infinite
NumberAnimation { from: 0.3; to: 0.7; duration: 2000; easing.type: Easing.InOutSine }
NumberAnimation { from: 0.7; to: 0.3; duration: 2000; easing.type: Easing.InOutSine }
}
}
GradientStop {
position: 1.0
color: Qt.lighter(Color.mPrimary, 1.2)
}
}
}
// Circular cutout
@ -44,8 +66,7 @@ Slider {
height: knobDiameter + cutoutExtra
radius: width / 2
color: root.cutoutColor !== undefined ? root.cutoutColor : Color.mSurface
x: Math.max(0, Math.min(parent.width - width, Math.round(root.visualPosition * (parent.width - root.knobDiameter) - cutoutExtra / 2)))
y: (parent.height - height) / 2
x: root.leftPadding + root.visualPosition * (root.availableWidth - root.knobDiameter) - cutoutExtra / 2
anchors.verticalCenter: parent.verticalCenter
}
}
@ -73,4 +94,4 @@ Slider {
}
}
}
}
}

View file

@ -1,61 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services
import qs.Widgets
RowLayout {
id: root
// Properties that mirror NSlider
property real from: 0
property real to: 1
property real value: 0
property real stepSize: 0.01
property var cutoutColor
property bool snapAlways: true
property real heightRatio: 0.75
property bool showPercentage: true
property string suffix: "%"
property int decimalPlaces: 0 // 0 for integers, 1 for one decimal place, etc.
// Signals
signal moved(real value)
signal pressedChanged(bool pressed)
spacing: Style.marginS * scaling
NSlider {
id: slider
Layout.fillWidth: true
from: root.from
to: root.to
value: root.value
stepSize: root.stepSize
cutoutColor: root.cutoutColor
snapAlways: root.snapAlways
heightRatio: root.heightRatio
stableWidth: true
minWidth: 200 * scaling
onMoved: root.moved(value)
onPressedChanged: root.pressedChanged(pressed)
}
NText {
id: percentageLabel
visible: root.showPercentage
text: {
if (root.decimalPlaces === 0) {
return Math.round(slider.value * 100) + root.suffix
} else {
return (slider.value * 100).toFixed(root.decimalPlaces) + root.suffix
}
}
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
Layout.preferredWidth: 50 * scaling
horizontalAlignment: Text.AlignRight
}
}

48
Widgets/NValueSlider.qml Normal file
View file

@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services
import qs.Widgets
RowLayout {
id: root
property real from: 0
property real to: 1
property real value: 0
property real stepSize: 0.01
property var cutoutColor: Color.mSurface
property bool snapAlways: true
property real heightRatio: 0.75
property string text: ""
// Signals
signal moved(real value)
signal pressedChanged(bool pressed, real value)
spacing: Style.marginL * scaling
NSlider {
id: slider
Layout.fillWidth: true
from: root.from
to: root.to
value: root.value
stepSize: root.stepSize
cutoutColor: root.cutoutColor
snapAlways: root.snapAlways
heightRatio: root.heightRatio
onMoved: root.moved(value)
onPressedChanged: root.pressedChanged(pressed, value)
}
NText {
visible: root.text !== ""
text: root.text
font.family: Settings.data.ui.fontFixed
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 40 * scaling
horizontalAlignment: Text.AlignRight
}
}