NSlider: now support label and description
- was a little tricky, got many crashed due to layout issue with Layout.fillWidth: true
This commit is contained in:
parent
5c9c61cf81
commit
d06679aad6
4 changed files with 180 additions and 197 deletions
|
|
@ -1,9 +1,10 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import qs.Services
|
||||
|
||||
Slider {
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
readonly property real scaling: Scaling.scale(screen)
|
||||
|
|
@ -11,80 +12,156 @@ Slider {
|
|||
readonly property real trackHeight: knobDiameter * 0.5
|
||||
readonly property real cutoutExtra: Style.baseWidgetSize * 0.1 * scaling
|
||||
|
||||
property string label: ""
|
||||
property string description: ""
|
||||
property string valueSuffix: ""
|
||||
property real from: 0.0
|
||||
property real to: 1.0
|
||||
property real stepSize: 0.01
|
||||
property real value: 0.0
|
||||
|
||||
// Optional color to cut the track beneath the knob (should match surrounding background)
|
||||
property var cutoutColor
|
||||
property var screen
|
||||
property bool snapAlways: true
|
||||
|
||||
snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease
|
||||
implicitHeight: Math.max(trackHeight, knobDiameter)
|
||||
signal pressedChanged(bool pressed, real value)
|
||||
signal moved(real value)
|
||||
|
||||
background: Rectangle {
|
||||
x: root.leftPadding
|
||||
y: root.topPadding + root.availableHeight / 2 - height / 2
|
||||
implicitWidth: Style.sliderWidth
|
||||
implicitHeight: trackHeight
|
||||
width: root.availableWidth
|
||||
height: implicitHeight
|
||||
radius: height / 2
|
||||
color: Colors.surfaceVariant
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginSmall * scaling
|
||||
|
||||
Rectangle {
|
||||
id: activeTrack
|
||||
width: root.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: Colors.accentPrimary
|
||||
radius: parent.radius
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginTiniest * scaling
|
||||
|
||||
NText {
|
||||
text: label
|
||||
font.pointSize: Style.fontSizeMedium * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Colors.textPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NText {
|
||||
text: description
|
||||
font.pointSize: Style.fontSizeSmall * scaling
|
||||
color: Colors.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
// Circular cutout
|
||||
Rectangle {
|
||||
id: knobCutout
|
||||
width: knobDiameter + cutoutExtra
|
||||
height: knobDiameter + cutoutExtra
|
||||
radius: width / 2
|
||||
color: root.cutoutColor !== undefined ? root.cutoutColor : Colors.backgroundPrimary
|
||||
x: Math.max(0, Math.min(parent.width - width,
|
||||
root.visualPosition * (parent.width - root.knobDiameter) - cutoutExtra / 2))
|
||||
y: (parent.height - height) / 2
|
||||
NText {
|
||||
text: {
|
||||
var v
|
||||
if (Number.isInteger(value)) {
|
||||
v = value
|
||||
} else {
|
||||
v = value.toFixed(2)
|
||||
}
|
||||
|
||||
if (valueSuffix != "") {
|
||||
return v + valueSuffix
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
font.pointSize: Style.fontSizeMedium * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Colors.textPrimary
|
||||
Layout.alignment: Qt.AlignBottom | Qt.AlignRight
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
handle: Item {
|
||||
width: knob.implicitWidth
|
||||
height: knob.implicitHeight
|
||||
x: root.leftPadding + root.visualPosition * (root.availableWidth - width)
|
||||
y: root.topPadding + root.availableHeight / 2 - height / 2
|
||||
Slider {
|
||||
id: slider
|
||||
|
||||
// Subtle shadow for a more polished look (keeps theme colors)
|
||||
MultiEffect {
|
||||
anchors.fill: knob
|
||||
source: knob
|
||||
shadowEnabled: true
|
||||
shadowColor: Colors.shadow
|
||||
shadowOpacity: 0.25
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 1
|
||||
shadowBlur: 8
|
||||
Layout.fillWidth: true
|
||||
from: root.from
|
||||
to: root.to
|
||||
stepSize: root.stepSize
|
||||
value: root.value
|
||||
snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease
|
||||
implicitWidth: root.width
|
||||
implicitHeight: Math.max(trackHeight, knobDiameter)
|
||||
onPressedChanged: {
|
||||
root.pressedChanged(slider.pressed, slider.value)
|
||||
}
|
||||
onMoved: {
|
||||
root.value = slider.value
|
||||
root.moved(value)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: knob
|
||||
implicitWidth: knobDiameter
|
||||
implicitHeight: knobDiameter
|
||||
radius: width * 0.5
|
||||
color: root.pressed ? Colors.surfaceVariant : Colors.surface
|
||||
border.color: Colors.accentPrimary
|
||||
border.width: Math.max(1, Style.borderThick * scaling)
|
||||
background: Rectangle {
|
||||
x: slider.leftPadding
|
||||
y: slider.topPadding + slider.availableHeight / 2 - height / 2
|
||||
implicitWidth: Style.sliderWidth
|
||||
implicitHeight: trackHeight
|
||||
width: slider.availableWidth
|
||||
height: implicitHeight
|
||||
radius: height / 2
|
||||
color: Colors.surfaceVariant
|
||||
|
||||
// Press feedback halo (using accent color, low opacity)
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 8 * scaling
|
||||
height: parent.height + 8 * scaling
|
||||
radius: width / 2
|
||||
id: activeTrack
|
||||
width: slider.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: Colors.accentPrimary
|
||||
opacity: root.pressed ? 0.16 : 0.0
|
||||
radius: parent.radius
|
||||
}
|
||||
|
||||
// Circular cutout
|
||||
Rectangle {
|
||||
id: knobCutout
|
||||
width: knobDiameter + cutoutExtra
|
||||
height: knobDiameter + cutoutExtra
|
||||
radius: width / 2
|
||||
color: slider.cutoutColor !== undefined ? slider.cutoutColor : Colors.backgroundPrimary
|
||||
x: Math.max(0, Math.min(parent.width - width,
|
||||
slider.visualPosition * (parent.width - knobDiameter) - cutoutExtra / 2))
|
||||
y: (parent.height - height) / 2
|
||||
}
|
||||
}
|
||||
|
||||
handle: Item {
|
||||
width: knob.implicitWidth
|
||||
height: knob.implicitHeight
|
||||
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
|
||||
y: slider.topPadding + slider.availableHeight / 2 - height / 2
|
||||
|
||||
// Subtle shadow for a more polished look (keeps theme colors)
|
||||
MultiEffect {
|
||||
anchors.fill: knob
|
||||
source: knob
|
||||
shadowEnabled: true
|
||||
shadowColor: Colors.shadow
|
||||
shadowOpacity: 0.25
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 1
|
||||
shadowBlur: 8
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: knob
|
||||
implicitWidth: knobDiameter
|
||||
implicitHeight: knobDiameter
|
||||
radius: width * 0.5
|
||||
color: slider.pressed ? Colors.surfaceVariant : Colors.surface
|
||||
border.color: Colors.accentPrimary
|
||||
border.width: Math.max(1, Style.borderThick * scaling)
|
||||
|
||||
// Press feedback halo (using accent color, low opacity)
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 8 * scaling
|
||||
height: parent.height + 8 * scaling
|
||||
radius: width / 2
|
||||
color: Colors.accentPrimary
|
||||
opacity: slider.pressed ? 0.16 : 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue