scrollbar rewrite
no binding loops no rotation transformations actual scrollbar is a loaded component: will be shared with scrolldecorator
This commit is contained in:
parent
6a19c2da5b
commit
8cccd7c834
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2011 by Daker Fernandes Pinheiro <dakerfp@gmail.com>
|
||||
* Copyright 2011 Marco Martin <mart@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
@ -26,22 +27,20 @@ Item {
|
||||
|
||||
// Common API
|
||||
property Flickable flickableItem: null
|
||||
property bool interactive
|
||||
property int orientation: Qt.Vertical
|
||||
property bool interactive: true
|
||||
|
||||
// Plasma API
|
||||
property int orientation: Qt.Vertical
|
||||
property bool animated: true
|
||||
property bool inverted: false
|
||||
property bool updateValueWhileDragging: true
|
||||
property alias stepSize: range.stepSize
|
||||
property alias pressed: mouseArea.pressed
|
||||
//property alias pressed: mouseArea.pressed
|
||||
property real scrollButtonInterval: 50
|
||||
|
||||
// Convinience API
|
||||
property bool _isVertical: orientation == Qt.Vertical
|
||||
property bool _showButtons: stepSize != 0
|
||||
property bool _inverted: _isVertical ?
|
||||
!scrollbar.inverted : scrollbar.inverted
|
||||
|
||||
implicitWidth: _isVertical ? 22 : 200
|
||||
implicitHeight: _isVertical ? 200 : 22
|
||||
@ -49,7 +48,7 @@ Item {
|
||||
// disabled scroll bars
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
|
||||
visible: flickableItem && handle.width < contents.width
|
||||
visible: flickableItem && internalLoader.handleEnabled
|
||||
|
||||
anchors {
|
||||
right: flickableItem.right
|
||||
@ -58,67 +57,161 @@ Item {
|
||||
bottom: flickableItem.bottom
|
||||
}
|
||||
|
||||
function incrementValue(increment) {
|
||||
if (!flickableItem)
|
||||
return;
|
||||
|
||||
if (_isVertical) {
|
||||
flickableItem.contentY = Math.min(flickableItem.contentHeight,
|
||||
flickableItem.contentY + increment);
|
||||
} else {
|
||||
flickableItem.contentX = Math.min(flickableItem.contentWidth,
|
||||
flickableItem.contentX + increment);
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
if (!enabled || !_isVertical)
|
||||
return;
|
||||
|
||||
if (_inverted)
|
||||
incrementValue(-stepSize);
|
||||
if (inverted)
|
||||
internalLoader.incrementValue(-stepSize);
|
||||
else
|
||||
incrementValue(stepSize);
|
||||
internalLoader.incrementValue(stepSize);
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
if (!enabled || !_isVertical)
|
||||
return;
|
||||
|
||||
if (_inverted)
|
||||
incrementValue(stepSize);
|
||||
if (inverted)
|
||||
internalLoader.incrementValue(stepSize);
|
||||
else
|
||||
incrementValue(-stepSize);
|
||||
internalLoader.incrementValue(-stepSize);
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
if (!enabled || _isVertical)
|
||||
return;
|
||||
|
||||
if (_inverted)
|
||||
incrementValue(stepSize);
|
||||
if (inverted)
|
||||
internalLoader.incrementValue(stepSize);
|
||||
else
|
||||
incrementValue(-stepSize);
|
||||
internalLoader.incrementValue(-stepSize);
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
if (!enabled || _isVertical)
|
||||
return;
|
||||
|
||||
if (_inverted)
|
||||
incrementValue(-stepSize);
|
||||
if (inverted)
|
||||
internalLoader.incrementValue(-stepSize);
|
||||
else
|
||||
incrementValue(stepSize);
|
||||
internalLoader.incrementValue(stepSize);
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: internalLoader
|
||||
anchors.fill: parent
|
||||
//property bool handleEnabled: _isVertical ? item.handle.height < item.contents.height : item.handle.width < item.contents.width
|
||||
property bool handleEnabled: _isVertical ? flickableItem.contentHeight > flickableItem.height : flickableItem.contentWidth > flickableItem.width
|
||||
|
||||
function incrementValue(increment)
|
||||
{
|
||||
if (!flickableItem)
|
||||
return;
|
||||
|
||||
if (_isVertical) {
|
||||
flickableItem.contentY = Math.max(0, Math.min(flickableItem.contentHeight,
|
||||
flickableItem.contentY + increment))
|
||||
} else {
|
||||
flickableItem.contentX = Math.max(0, Math.min(flickableItem.contentWidth,
|
||||
flickableItem.contentX + increment))
|
||||
}
|
||||
}
|
||||
|
||||
RangeModel {
|
||||
id: range
|
||||
|
||||
minimumValue: 0
|
||||
maximumValue: {
|
||||
var diff;
|
||||
if (_isVertical) {
|
||||
diff = flickableItem.contentHeight - flickableItem.height
|
||||
} else {
|
||||
diff = flickableItem.contentWidth - flickableItem.width
|
||||
}
|
||||
|
||||
return Math.max(0, diff)
|
||||
}
|
||||
|
||||
stepSize: 10
|
||||
inverted: scrollbar.inverted
|
||||
positionAtMinimum: 0
|
||||
positionAtMaximum: {
|
||||
if (_isVertical) {
|
||||
internalLoader.item.contents.height - internalLoader.item.handle.height
|
||||
} else {
|
||||
internalLoader.item.contents.width - internalLoader.item.handle.width
|
||||
}
|
||||
}
|
||||
value: _isVertical ? flickableItem.contentY : flickableItem.contentX
|
||||
onValueChanged: {
|
||||
if (flickableItem.moving) {
|
||||
return
|
||||
}
|
||||
|
||||
if (_isVertical) {
|
||||
flickableItem.contentY = value
|
||||
} else {
|
||||
flickableItem.contentX = value
|
||||
}
|
||||
}
|
||||
|
||||
position: _isVertical ? internalLoader.item.handle.y : internalLoader.item.handle.x
|
||||
|
||||
onPositionChanged: {
|
||||
if (internalLoader.item.mouseArea.pressed) {
|
||||
return
|
||||
}
|
||||
|
||||
if (_isVertical) {
|
||||
internalLoader.item.handle.y = position
|
||||
} else {
|
||||
internalLoader.item.handle.x = position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: Component {
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
imagePath:"widgets/scrollbar"
|
||||
prefix: _isVertical ? "background-vertical" : "background-horizontal"
|
||||
|
||||
property Item handle: handle
|
||||
|
||||
property Item contents: contents
|
||||
Item {
|
||||
id: contents
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: _isVertical || stepSize <= 0 ? 0 : leftButton.width
|
||||
rightMargin: _isVertical || stepSize <= 0 ? 0 : rightButton.width
|
||||
topMargin: _isVertical && stepSize > 0 ? leftButton.height : 0
|
||||
bottomMargin: _isVertical && stepSize > 0 ? rightButton.height : 0
|
||||
}
|
||||
|
||||
width: _isVertical ? scrollbar.height : scrollbar.width
|
||||
height: _isVertical ? scrollbar.width : scrollbar.height
|
||||
rotation: _isVertical ? -90 : 0
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: handle
|
||||
imagePath:"widgets/scrollbar"
|
||||
prefix: {
|
||||
if (mouseArea.pressed) {
|
||||
return "sunken-slider"
|
||||
}
|
||||
|
||||
if (scrollbar.activeFocus || mouseArea.containsMouse) {
|
||||
return "mouseover-slider"
|
||||
} else {
|
||||
return "slider"
|
||||
}
|
||||
}
|
||||
|
||||
property int length: _isVertical? flickableItem.visibleArea.heightRatio * parent.height : flickableItem.visibleArea.widthRatio * parent.width
|
||||
|
||||
width: _isVertical ? parent.width : length
|
||||
height: _isVertical ? length : parent.height
|
||||
}
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
PlasmaCore.Svg {
|
||||
id: scrollbarSvg
|
||||
imagePath: "widgets/scrollbar"
|
||||
@ -126,26 +219,31 @@ Item {
|
||||
|
||||
PlasmaCore.SvgItem {
|
||||
id: leftButton
|
||||
visible: stepSize > 0
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: _isVertical ? undefined : parent.left
|
||||
verticalCenter: _isVertical ? undefined : parent.verticalCenter
|
||||
top: _isVertical ? 0 : undefined
|
||||
horizontalCenter: _isVertical ? parent.horizontalCenter : undefined
|
||||
}
|
||||
height: 18
|
||||
width: _showButtons ? 18 : 0
|
||||
svg: scrollbarSvg
|
||||
elementId: {
|
||||
if (leftMousArea.pressed)
|
||||
return "sunken-arrow-left";
|
||||
if (leftMouseArea.pressed) {
|
||||
return _isVertical ? "sunken-arrow-up" : "sunken-arrow-left"
|
||||
}
|
||||
|
||||
if (scrollbar.activeFocus || leftMousArea.containsMouse)
|
||||
return "mouseover-arrow-left";
|
||||
else
|
||||
return "arrow-left";
|
||||
if (scrollbar.activeFocus || leftMouseArea.containsMouse) {
|
||||
return _isVertical ? "mouseover-arrow-up" : "mouseover-arrow-left"
|
||||
} else {
|
||||
return _isVertical ? "mouseover-arrow-up" : "arrow-left"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: leftMousArea
|
||||
id: leftMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
enabled: scrollbar.enabled
|
||||
@ -155,11 +253,12 @@ Item {
|
||||
running: parent.pressed
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
scrollbar.forceActiveFocus();
|
||||
if (_inverted)
|
||||
incrementValue(stepSize);
|
||||
else
|
||||
incrementValue(-stepSize);
|
||||
scrollbar.forceActiveFocus()
|
||||
if (inverted) {
|
||||
internalLoader.incrementValue(stepSize);
|
||||
} else {
|
||||
internalLoader.incrementValue(-stepSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,26 +266,31 @@ Item {
|
||||
|
||||
PlasmaCore.SvgItem {
|
||||
id: rightButton
|
||||
visible: stepSize > 0
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: _isVertical ? undefined : parent.right
|
||||
verticalCenter: _isVertical ? undefined : parent.verticalCenter
|
||||
bottom: _isVertical ? parent.bottom : undefined
|
||||
horizontalCenter: _isVertical ? parent.horizontalCenter : undefined
|
||||
}
|
||||
height: 18
|
||||
width: _showButtons ? 18 : 0
|
||||
svg: scrollbarSvg
|
||||
elementId: {
|
||||
if (rightMousArea.pressed)
|
||||
return "sunken-arrow-right";
|
||||
if (leftMouseArea.pressed) {
|
||||
return _isVertical ? "sunken-arrow-down" : "sunken-arrow-right"
|
||||
}
|
||||
|
||||
if (scrollbar.activeFocus || rightMousArea.containsMouse)
|
||||
return "mouseover-arrow-right";
|
||||
else
|
||||
return "arrow-right";
|
||||
if (scrollbar.activeFocus || leftMouseArea.containsMouse) {
|
||||
return _isVertical ? "mouseover-arrow-down" : "mouseover-arrow-right"
|
||||
} else {
|
||||
return _isVertical ? "mouseover-arrow-down" : "arrow-right"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rightMousArea
|
||||
id: rightMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
enabled: scrollbar.enabled
|
||||
@ -197,161 +301,62 @@ Item {
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
scrollbar.forceActiveFocus();
|
||||
if (_inverted)
|
||||
incrementValue(-stepSize);
|
||||
if (inverted)
|
||||
internalLoader.incrementValue(-stepSize);
|
||||
else
|
||||
incrementValue(stepSize);
|
||||
internalLoader.incrementValue(stepSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contents
|
||||
|
||||
anchors {
|
||||
left: leftButton.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
right: rightButton.left
|
||||
}
|
||||
|
||||
RangeModel {
|
||||
id: range
|
||||
|
||||
minimumValue: 0
|
||||
maximumValue: {
|
||||
var diff;
|
||||
if (_isVertical)
|
||||
diff = flickableItem.contentHeight - flickableItem.height;
|
||||
else
|
||||
diff = flickableItem.contentWidth - flickableItem.width;
|
||||
|
||||
return Math.max(0, diff);
|
||||
}
|
||||
stepSize: 10
|
||||
inverted: _inverted
|
||||
positionAtMinimum: 0 + handle.width / 2
|
||||
positionAtMaximum: contents.width - handle.width / 2
|
||||
value: _isVertical ? flickableItem.contentY : flickableItem.contentX
|
||||
onValueChanged: {
|
||||
if (flickableItem.flicking)
|
||||
return;
|
||||
|
||||
if (_isVertical)
|
||||
flickableItem.contentY = value;
|
||||
else
|
||||
flickableItem.contentX = value;
|
||||
}
|
||||
position: handle.x
|
||||
onPositionChanged: { handle.x = position; }
|
||||
}
|
||||
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: groove
|
||||
|
||||
anchors.fill: parent
|
||||
imagePath: "widgets/scrollbar"
|
||||
prefix: "background-horizontal"
|
||||
}
|
||||
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: handle
|
||||
|
||||
transform: Translate { x: - handle.width / 2 }
|
||||
x: fakeHandle.x
|
||||
anchors.verticalCenter: groove.verticalCenter
|
||||
width: {
|
||||
var ratio;
|
||||
if (_isVertical)
|
||||
ratio = flickableItem.visibleArea.heightRatio;
|
||||
else
|
||||
ratio = flickableItem.visibleArea.widthRatio;
|
||||
|
||||
return ratio * parent.width;
|
||||
}
|
||||
height: parent.height - margins.top // TODO: check mergin
|
||||
imagePath: "widgets/scrollbar"
|
||||
prefix: {
|
||||
if (scrollbar.pressed)
|
||||
return "sunken-slider";
|
||||
|
||||
if (scrollbar.activeFocus || mouseArea.containsMouse)
|
||||
return "mouseover-slider";
|
||||
else
|
||||
return "slider";
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
id: behavior
|
||||
enabled: !mouseArea.drag.active && scrollbar.animated &&
|
||||
!flickableItem.flicking
|
||||
|
||||
PropertyAnimation {
|
||||
duration: behavior.enabled ? 150 : 0
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: fakeHandle
|
||||
width: handle.width
|
||||
height: handle.height
|
||||
transform: Translate { x: - handle.width / 2 }
|
||||
}
|
||||
|
||||
property MouseArea mouseArea: mouseArea
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.fill: contents
|
||||
enabled: scrollbar.enabled
|
||||
hoverEnabled: true
|
||||
drag {
|
||||
target: fakeHandle
|
||||
axis: Drag.XAxis
|
||||
target: handle
|
||||
axis: _isVertical ? Drag.YAxis : Drag.XAxis
|
||||
minimumX: range.positionAtMinimum
|
||||
maximumX: range.positionAtMaximum
|
||||
minimumY: range.positionAtMinimum
|
||||
maximumY: range.positionAtMaximum
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
if (_isVertical) {
|
||||
// Clamp the value
|
||||
var newY = Math.max(mouse.y, drag.minimumY);
|
||||
newY = Math.min(newY, drag.maximumY);
|
||||
|
||||
// Debounce the press: a press event inside the handler will not
|
||||
// change its position, the user needs to drag it.
|
||||
if (newY > handle.y + handle.height) {
|
||||
handle.y = mouse.y - handle.height
|
||||
} else if (newY < handle.y) {
|
||||
handle.y = mouse.y
|
||||
}
|
||||
} else {
|
||||
// Clamp the value
|
||||
var newX = Math.max(mouse.x, drag.minimumX);
|
||||
newX = Math.min(newX, drag.maximumX);
|
||||
|
||||
// Debounce the press: a press event inside the handler will not
|
||||
// change its position, the user needs to drag it.
|
||||
if (Math.abs(newX - fakeHandle.x) > handle.width / 2)
|
||||
range.position = newX;
|
||||
if (newX > handle.x + handle.width) {
|
||||
handle.x = mouse.x - handle.width
|
||||
} else if (newX < handle.x) {
|
||||
handle.x = mouse.x
|
||||
}
|
||||
}
|
||||
|
||||
scrollbar.forceActiveFocus();
|
||||
}
|
||||
onReleased: {
|
||||
// If we don't update while dragging, this is the only
|
||||
// moment that the range is updated.
|
||||
if (!scrollbar.updateValueWhileDragging)
|
||||
range.position = fakeHandle.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Range position normally follow fakeHandle, except when
|
||||
// 'updateValueWhileDragging' is false. In this case it will only follow
|
||||
// if the user is not pressing the handle.
|
||||
Binding {
|
||||
when: updateValueWhileDragging || !mouseArea.pressed
|
||||
target: range
|
||||
property: "position"
|
||||
value: fakeHandle.x
|
||||
}
|
||||
|
||||
// During the drag, we simply ignore position set from the range, this
|
||||
// means that setting a value while dragging will not "interrupt" the
|
||||
// dragging activity.
|
||||
Binding {
|
||||
when: !mouseArea.drag.active
|
||||
target: fakeHandle
|
||||
property: "x"
|
||||
value: range.position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user