pragma ComponentBehavior: Bound import QtQuick import QtQuick.Templates import qs.Helpers import ZShell.Config ScrollBar { id: root required property Flickable flickable readonly property bool isHorizontal: flickable && flickable.ScrollBar.horizontal === root readonly property real axisSize: isHorizontal ? flickable.width : flickable.height readonly property real axisContentSize: isHorizontal ? flickable.contentWidth : flickable.contentHeight readonly property real axisContentPos: isHorizontal ? (flickable.contentX - flickable.originX) : (flickable.contentY - flickable.originY) readonly property real axisLength: isHorizontal ? root.width : root.height readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize) readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize) readonly property real nonAnimHeight: root.axisSize / root.axisContentSize readonly property real nonAnimY: root.axisContentPos / root.axisContentSize readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight) readonly property bool reversed: isHorizontal ? (flickable instanceof ListView && flickable.layoutDirection === Qt.RightToLeft) : (flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop) property bool shouldBeActive readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0 enabled: !Visibilities.getForActive().isDrawing implicitWidth: size === 1 ? 0 : isHorizontal ? 0 : Tokens.padding.extraSmall * 2 implicitHeight: size === 1 ? 0 : isHorizontal ? Tokens.padding.extraSmall * 2 : 0 contentItem: Item {} Behavior on position { enabled: !fullMouse.pressed Anim {} } onHoveredChanged: { if (hovered) shouldBeActive = hovered; else hideDelay.restart(); } Connections { function onMovingChanged() { if (root.flickable.moving) root.shouldBeActive = true; else hideDelay.restart(); } target: root.flickable } Loader { anchors.fill: parent active: root.size < 1 sourceComponent: root.isHorizontal ? horizontalTrack : verticalTrack } Component { id: verticalTrack VerticalScrollBarTrack { scrollBar: root mouseArea: fullMouse } } Component { id: horizontalTrack HorizontalScrollBarTrack { scrollBar: root mouseArea: fullMouse } } Timer { id: hideDelay interval: 600 onTriggered: root.shouldBeActive = root.flickable.moving || root.hovered } CustomMouseArea { id: fullMouse property real pressOffset: 0 function contentPosFromThumbStart(thumbStart) { var visualPos = root.effectiveTravel > 0 ? thumbStart / root.travelScale : 0; return root.reversed ? (visualPos - 1) * root.axisContentSize : visualPos * root.axisContentSize; } function updateFromEvent(event) { var eventPos = root.isHorizontal ? event.x : event.y; var posInTrack = eventPos / root.axisLength; var thumbStart = posInTrack - pressOffset; thumbStart = Math.max(0, Math.min(root.effectiveTravel, thumbStart)); var newPos = contentPosFromThumbStart(thumbStart); if (root.isHorizontal) root.flickable.contentX = newPos + root.flickable.originX; else root.flickable.contentY = newPos + root.flickable.originY; } function visualThumbStart() { const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY; return visualPos * root.travelScale; } function onWheel(event: WheelEvent): void { if (root.horizontal) { event.accepted = false; return; } var delta = event.angleDelta.y > 0 ? -0.1 : 0.1; var newPos = Math.max(0, Math.min(1 - root.size, root.position + delta)); root.position = newPos; } anchors.fill: parent cursorShape: undefined hoverEnabled: true preventStealing: true onPositionChanged: event => { if (pressed) updateFromEvent(event); } onPressed: event => { var currentStart = visualThumbStart(); var currentEnd = currentStart + root.effectiveSize; var eventPos = root.isHorizontal ? event.x : event.y; var clickPos = eventPos / root.axisLength; var clickedInsideThumb = clickPos >= currentStart && clickPos <= currentEnd; pressOffset = clickedInsideThumb ? (clickPos - currentStart) : root.effectiveSize / 2; updateFromEvent(event); } } }