import QtQuick import QtQuick.Templates import ZShell.Config import qs.Services Slider { id: root property color color: Colors.palette.m3secondary required property string icon property bool initialized: false readonly property bool isHorizontal: orientation === Qt.Horizontal readonly property bool isVertical: orientation === Qt.Vertical property real multiplier: 100 property real oldValue property Component trackContent orientation: Qt.Vertical background: CustomRect { id: groove color: Colors.layer(Colors.palette.m3surfaceContainer, 2) height: root.availableHeight radius: Tokens.rounding.full width: root.availableWidth x: root.leftPadding y: root.topPadding Loader { id: trackLoader anchors.fill: parent sourceComponent: root.trackContent onLoaded: { if (!item) return; item.rootSlider = root; item.groove = groove; item.handleItem = handle; } } } handle: Item { id: handle property alias moving: icon.moving implicitHeight: Math.min(root.width, root.height) implicitWidth: Math.min(root.width, root.height) x: root.isHorizontal ? root.leftPadding + root.visualPosition * (root.availableWidth - width) : root.leftPadding + (root.availableWidth - width) / 2 y: root.isVertical ? root.topPadding + root.visualPosition * (root.availableHeight - height) : root.topPadding + (root.availableHeight - height) / 2 Elevation { anchors.fill: parent level: handleInteraction.containsMouse ? 2 : 1 radius: rect.radius } CustomRect { id: rect anchors.fill: parent color: Colors.palette.m3inverseSurface radius: Tokens.rounding.full MouseArea { id: handleInteraction acceptedButtons: Qt.NoButton anchors.fill: parent cursorShape: Qt.PointingHandCursor hoverEnabled: true } MaterialIcon { id: icon property bool moving anchors.centerIn: parent anchors.verticalCenterOffset: 1 text: moving ? Math.round(root.value * 100) : root.icon color: Colors.palette.m3inverseOnSurface font: moving ? Tokens.font.body.small : Tokens.font.icon.medium Behavior on moving { SequentialAnimation { Anim { target: icon property: "scale" to: 0.3 duration: Tokens.anim.durations.small / 2 easing: Tokens.anim.standardAccel } PropertyAction {} Anim { target: icon property: "scale" to: 1 duration: Tokens.anim.durations.normal / 2 easing: Tokens.anim.standardDecel } } } } } } Behavior on value { Anim { type: Anim.StandardLarge } } onPressedChanged: handle.moving = pressed onValueChanged: { if (!initialized) { initialized = true; return; } if (Math.abs(value - oldValue) < 0.01) return; oldValue = value; handle.moving = true; stateChangeDelay.restart(); } Timer { id: stateChangeDelay interval: 500 onTriggered: { if (!root.pressed) handle.moving = false; } } }