148 lines
4.2 KiB
QML
148 lines
4.2 KiB
QML
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);
|
|
}
|
|
}
|
|
}
|