import QtQuick import qs.Commons Item { id: root property QtObject bar: null property real value: 0 property real minimum: 0 property real maximum: 1 property real step: 0.05 property bool integer: false property color trackColor: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "#333" property color fillColor: bar ? bar.foreground : Color.foreground property color knobColor: bar ? bar.foreground : Color.foreground property bool dragging: false property real trackHeight: Math.max(4, Math.round(Style.spacing.controlHeight * 0.11)) property real knobSize: Math.max(14, Math.round(Style.spacing.controlHeight * 0.38)) property real liveValue: value onValueChanged: if (!dragging) liveValue = value signal moved(real value) signal released(real value) implicitWidth: Style.space(200) implicitHeight: Math.max(Style.space(22), knobSize + Style.spacing.md) readonly property real range: Math.max(0.0001, maximum - minimum) readonly property real progress: Math.max(0, Math.min(1, (liveValue - minimum) / range)) readonly property bool _hot: mouseArea.containsMouse || root.dragging Rectangle { id: track anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.right: parent.right height: root.trackHeight radius: height / 2 color: root.trackColor } Rectangle { id: fill anchors.verticalCenter: track.verticalCenter anchors.left: track.left height: track.height radius: track.radius color: root.fillColor width: track.width * root.progress Behavior on width { enabled: !root.dragging NumberAnimation { duration: 140; easing.type: Easing.OutCubic } } } Rectangle { id: knob width: root.knobSize height: root.knobSize radius: root.knobSize / 2 color: root.knobColor border.color: root.bar ? root.bar.background : "#101315" border.width: Math.max(1, Style.space(2)) anchors.verticalCenter: track.verticalCenter x: Math.max(0, Math.min(track.width - width, track.width * root.progress - width / 2)) scale: root._hot ? 1.15 : 1.0 Behavior on x { enabled: !root.dragging NumberAnimation { duration: 140; easing.type: Easing.OutCubic } } Behavior on scale { NumberAnimation { duration: 110; easing.type: Easing.OutCubic } } } MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton function valueFromX(x) { var clamped = Math.max(0, Math.min(track.width, x)) var raw = root.minimum + (clamped / track.width) * root.range if (root.integer) raw = Math.round(raw) return Math.max(root.minimum, Math.min(root.maximum, raw)) } onPressed: function(mouse) { root.dragging = true var next = valueFromX(mouse.x) root.liveValue = next root.moved(next) } onPositionChanged: function(mouse) { if (!root.dragging) return var next = valueFromX(mouse.x) root.liveValue = next root.moved(next) } onReleased: function(mouse) { root.dragging = false root.released(root.liveValue) root.liveValue = root.value } onWheel: function(wheel) { var delta = wheel.angleDelta.y > 0 ? root.step : -root.step var next = Math.max(root.minimum, Math.min(root.maximum, root.liveValue + delta)) if (root.integer) next = Math.round(next) root.liveValue = next root.moved(next) root.released(next) } } }