Created
June 22, 2022 23:31
-
-
Save danieloneill/0034098d06ead35d8048da635ea8f89c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import QtQuick 2.4 | |
Item { | |
id: scrollbar | |
property QtObject target //< The flickable to control | |
property bool autoHide: true //< If true, the bar will be hidden unless mouseover or moving | |
property real visibleOpacity: 0.9 //< When visible (not hidden), how "opaque" are we? | |
property int orientation: Qt.Vertical //< Either Qt.Vertical or Qt.Horizontal | |
function decPos(amount) { | |
if( !amount ) amount = 0.10; | |
let newPos = position - amount; | |
if( newPos < 0 ) | |
newPos = 0; | |
scrollbar.position = newPos; | |
recalculateHandle(); | |
} | |
function incPos(amount) { | |
if( !amount ) amount = 0.10; | |
const max = 1-scrollbar.size; | |
let newPos = position + amount; | |
if( newPos >= max ) | |
newPos = max; | |
scrollbar.position = newPos; | |
recalculateHandle(); | |
} | |
// These don't normally need to be set: | |
property real minimumSize: 0.1 //< The minimum size of the handle | |
property real position: 0 //< The position of the scroller (between 0.0 and 1.0) | |
// The rest, as they say, is logic. | |
implicitHeight: orientation == Qt.Horizontal ? 24 : parent.height | |
implicitWidth: orientation == Qt.Horizontal ? parent.width : 24 | |
property real size: 0.5 | |
onSizeChanged: recalculateHandle(); | |
onPositionChanged: { | |
updatePosition(); | |
recalculateHandle(); | |
} | |
MouseArea { | |
id: hoverTester | |
anchors.fill: scrollbar | |
hoverEnabled: true | |
} | |
Item { | |
id: scrollerHint | |
anchors.fill: parent | |
opacity: (scrollbar.size < 1) ? (autoHide ? (hoverTester.containsMouse || scrollbar.target.dragging ? scrollbar.visibleOpacity : 0) : scrollbar.visibleOpacity) : 0 | |
Behavior on opacity { | |
NumberAnimation { | |
target: scrollerHint | |
property: 'opacity' | |
duration: 200 | |
} | |
} | |
Rectangle { | |
id: scrollerHintBG | |
y: scrollbar.orientation === Qt.Horizontal ? parent.height * 0.5 - (height * 0.5) : 24 | |
x: scrollbar.orientation === Qt.Horizontal ? 24 : parent.width * 0.5 - (width * 0.5) | |
width: scrollbar.orientation === Qt.Horizontal ? parent.width - 48 : 6 | |
height: scrollbar.orientation === Qt.Horizontal ? 6 : parent.height - 48 | |
radius: 3 | |
color: '#eadafa' | |
Rectangle { | |
id: handle | |
y: scrollbar.orientation === Qt.Horizontal ? parent.height * 0.5 - (height * 0.5) : handle.y | |
x: scrollbar.orientation === Qt.Horizontal ? handle.x : parent.width * 0.5 - (width * 0.5) | |
width: scrollbar.orientation === Qt.Horizontal ? handle.width : 6 | |
height: scrollbar.orientation === Qt.Horizontal ? 6 : handle.height | |
color: '#3a2a4a' | |
radius: 3 | |
MouseArea { | |
id: handleDragger | |
anchors.fill: parent | |
cursorShape: scrollbar.orientation == Qt.Horizontal ? Qt.SizeHorCursor : Qt.SizeVerCursor | |
drag { | |
target: parent | |
axis: scrollbar.orientation == Qt.Horizontal ? Drag.XAxis : Drag.YAxis | |
minimumX: 0 | |
maximumX: scrollerHintBG.width - handle.width | |
minimumY: 0 | |
maximumY: scrollerHintBG.height - handle.height | |
smoothed: true | |
threshold: 0 | |
} | |
onMouseXChanged: function(ev) { if( scrollbar.orientation == Qt.Horizontal ) dragged(ev) } | |
onMouseYChanged: function(ev) { if( scrollbar.orientation != Qt.Horizontal ) dragged(ev) } | |
function dragged(ev) { | |
if( !drag.active ) | |
return; | |
const myPos = ( scrollbar.orientation == Qt.Horizontal ? handle.x : handle.y ); | |
const maxPos = ( scrollbar.orientation == Qt.Horizontal ? (scrollerHintBG.width-handle.width) : (scrollerHintBG.height-handle.height) ); | |
const newratio = myPos / maxPos; | |
const newpos = (1-scrollbar.size) * newratio; | |
scrollbar.position = newpos; | |
} | |
} // handleDragger | |
} // handle | |
} // scrollerHintBG | |
} | |
function recalculateHandlePosition() | |
{ | |
let newHandlePos = scrollbar.position * (scrollbar.orientation == Qt.Horizontal ? scrollbar.width : scrollbar.height); | |
if( scrollbar.orientation == Qt.Horizontal ) | |
handle.x = newHandlePos; | |
else | |
handle.y = newHandlePos; | |
} | |
function recalculateHandleSize() | |
{ | |
const maxSize = (scrollbar.orientation == Qt.Horizontal ? scrollbar.width : scrollbar.height); | |
let newHandleSize = maxSize * scrollbar.size; | |
if( newHandleSize > maxSize ) | |
newHandleSize = maxSize; | |
else if( newHandleSize < 10 ) | |
newHandleSize = 10; | |
if( scrollbar.orientation == Qt.Horizontal ) | |
handle.width = newHandleSize; | |
else | |
handle.height = newHandleSize; | |
} | |
function recalculateHandle() { | |
if( handleDragger.drag.active ) | |
return; | |
recalculateHandlePosition(); | |
recalculateHandleSize(); | |
} | |
Connections { | |
target: scrollbar.target | |
function onContentWidthChanged() { scrollbar.updateSize(); } | |
function onContentHeightChanged() { scrollbar.updateSize(); } | |
function onWidthChanged() { scrollbar.updateSize(); } | |
function onHeightChanged() { scrollbar.updateSize(); } | |
function onContentXChanged() { recentre(); } | |
function onContentYChanged() { recentre(); } | |
} | |
function recentre() { | |
// Called to move the handle position to match wherever contentX is: | |
if( scrollbar.orientation == Qt.Horizontal ) | |
{ | |
if( scrollbar.target.width >= scrollbar.target.contentWidth ) | |
{ | |
scrollbar.position = 0; | |
scrollbar.target.contentX = 0; | |
} | |
else if( scrollbar.target.contentX > scrollbar.target.contentWidth - scrollbar.target.width ) | |
scrollbar.position = (1-scrollbar.size); | |
else { | |
const diff = scrollbar.target.contentWidth - scrollbar.target.width; | |
const cquotient = scrollbar.target.contentX / diff; | |
const hquotient = (1-scrollbar.size); | |
const newpos = hquotient * cquotient; | |
if( newpos >= 0 ) | |
scrollbar.position = newpos; | |
} | |
} else { | |
if( scrollbar.target.height >= scrollbar.target.contentHeight ) | |
{ | |
scrollbar.position = 0; | |
scrollbar.target.contentY = 0; | |
} | |
else if( scrollbar.target.contentY > scrollbar.target.contentHeight - scrollbar.target.height ) | |
scrollbar.position = (1-scrollbar.size); | |
else { | |
const diff = scrollbar.target.contentHeight - scrollbar.target.height; | |
const cquotient = scrollbar.target.contentY / diff; | |
const hquotient = (1-scrollbar.size); | |
const newpos = hquotient * cquotient; | |
if( newpos >= 0 ) | |
scrollbar.position = newpos; | |
} | |
} | |
} | |
function updateSize() { | |
// Find the ratio to calculate our handle size: | |
const ratio = (scrollbar.orientation == Qt.Horizontal ? (scrollbar.target.width / scrollbar.target.contentWidth) : (scrollbar.target.height / scrollbar.target.contentHeight)); | |
scrollbar.size = ratio <= 1 ? ratio : 1 | |
} | |
function updatePosition() { | |
const quotient = (1-scrollbar.size); | |
if( quotient <= 0 ) | |
return; | |
const handlepos = scrollbar.position / quotient; | |
const viewportDiff = (scrollbar.orientation == Qt.Horizontal ? (scrollbar.target.contentWidth - scrollbar.target.width) : (scrollbar.target.contentHeight - scrollbar.target.height)) | |
const diff = viewportDiff * handlepos; | |
if( scrollbar.orientation == Qt.Horizontal ) | |
scrollbar.target.contentX = diff; | |
else | |
scrollbar.target.contentY = diff; | |
} | |
Component.onCompleted: recalculateHandle(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import QtQuick | |
Window { | |
width: 640 | |
height: 480 | |
visible: true | |
title: 'Scrollbar Test' | |
Flickable { | |
id: flicker | |
anchors.fill: parent | |
anchors.margins: 10 | |
contentWidth: innerImage.width | |
contentHeight: innerImage.height | |
Image { | |
id: innerImage | |
source: 'https://images.alphacoders.com/988/988021.jpg' | |
} | |
} | |
Scrollbar { | |
id: scrollH | |
orientation: Qt.Horizontal | |
autoHide: true | |
visibleOpacity: 0.9 | |
anchors { | |
left: flicker.left | |
right: flicker.right | |
bottom: flicker.bottom | |
} | |
target: flicker // Must be a Flickable | |
} | |
Scrollbar { | |
id: scrollV | |
orientation: Qt.Vertical | |
autoHide: true | |
visibleOpacity: 0.9 | |
anchors { | |
top: flicker.top | |
right: flicker.right | |
bottom: flicker.bottom | |
} | |
target: flicker // Gotta be a Flickable | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment