122 lines
3.8 KiB
QML
122 lines
3.8 KiB
QML
// A GNOME-style slider: a thick rounded track with an inline leading icon,
|
|
// used for volume and brightness in the quick settings panel.
|
|
//
|
|
// Deliberately not QtQuick.Controls.Slider — that pulls in a style plugin and
|
|
// fights the theme. This is ~50 lines and does exactly what's needed.
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Widgets
|
|
import qs.config
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property real value: 0 // 0.0 .. 1.0
|
|
property string icon: ""
|
|
property bool enabled: true
|
|
|
|
// Emitted continuously while dragging.
|
|
signal moved(real value)
|
|
|
|
implicitWidth: 240
|
|
implicitHeight: 32
|
|
|
|
opacity: enabled ? 1.0 : 0.45
|
|
|
|
// ThemedIcon, not a bare IconImage — see the note in Toggle.qml: Adwaita
|
|
// symbolic icons have a hardcoded near-black fill that Qt will not recolour.
|
|
ThemedIcon {
|
|
id: leadingIcon
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
size: 18
|
|
icon: root.icon
|
|
tint: Theme.fg
|
|
visible: root.icon !== ""
|
|
}
|
|
|
|
Rectangle {
|
|
id: track
|
|
anchors.left: leadingIcon.visible ? leadingIcon.right : parent.left
|
|
anchors.leftMargin: leadingIcon.visible ? 10 : 0
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
height: 10
|
|
radius: height / 2
|
|
color: Theme.alpha(Theme.fg, 0.12)
|
|
border.width: 0
|
|
|
|
Rectangle {
|
|
id: fill
|
|
anchors.left: parent.left
|
|
anchors.top: parent.top
|
|
anchors.bottom: parent.bottom
|
|
width: parent.width * Math.max(0, Math.min(1, root.value))
|
|
radius: parent.radius
|
|
border.width: 0
|
|
|
|
// The gradient is anchored to the full track, not to the fill, so
|
|
// the colour at a given position never moves as the value changes —
|
|
// dragging reveals the prism rather than squashing it.
|
|
gradient: Gradient {
|
|
orientation: Gradient.Horizontal
|
|
GradientStop {
|
|
position: 0.0
|
|
color: Theme.accent
|
|
}
|
|
GradientStop {
|
|
position: 1.0
|
|
color: Theme.mix(Theme.accent, Theme.accentSecondary, Math.max(0.001, Math.min(1, root.value)))
|
|
}
|
|
}
|
|
|
|
// Only animates when the value changes from outside (e.g. a media
|
|
// key); dragging sets it directly and reads as instant.
|
|
Behavior on width {
|
|
enabled: !drag.pressed
|
|
NumberAnimation {
|
|
duration: Theme.durFast
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: knob
|
|
width: 16
|
|
height: 16
|
|
radius: 8
|
|
border.width: 0
|
|
color: Theme.fg
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
x: Math.max(0, Math.min(parent.width - width, fill.width - width / 2))
|
|
visible: drag.containsMouse || drag.pressed
|
|
}
|
|
|
|
MouseArea {
|
|
id: drag
|
|
anchors.fill: parent
|
|
anchors.margins: -8 // easier to grab than a 10px track
|
|
enabled: root.enabled
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
|
|
function apply(mouseX) {
|
|
const ratio = Math.max(0, Math.min(1, (mouseX + 8) / track.width));
|
|
root.moved(ratio);
|
|
}
|
|
|
|
onPressed: event => apply(event.x)
|
|
onPositionChanged: event => {
|
|
if (pressed)
|
|
apply(event.x);
|
|
}
|
|
onWheel: event => {
|
|
const step = event.angleDelta.y > 0 ? 0.05 : -0.05;
|
|
root.moved(Math.max(0, Math.min(1, root.value + step)));
|
|
}
|
|
}
|
|
}
|
|
}
|