From 8cccd7c834fe02ee48b68bc3e3c7db9e4b4617b5 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 9 Nov 2011 14:21:54 +0100 Subject: [PATCH] scrollbar rewrite no binding loops no rotation transformations actual scrollbar is a loaded component: will be shared with scrolldecorator --- .../plasmacomponents/qml/ScrollBar.qml | 499 +++++++++--------- 1 file changed, 252 insertions(+), 247 deletions(-) diff --git a/declarativeimports/plasmacomponents/qml/ScrollBar.qml b/declarativeimports/plasmacomponents/qml/ScrollBar.qml index 9a5b637c0..f1588d5cc 100644 --- a/declarativeimports/plasmacomponents/qml/ScrollBar.qml +++ b/declarativeimports/plasmacomponents/qml/ScrollBar.qml @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 by Daker Fernandes Pinheiro +* Copyright 2011 Marco Martin * * 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,300 +57,306 @@ 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 - Item { + function incrementValue(increment) + { + if (!flickableItem) + return; - width: _isVertical ? scrollbar.height : scrollbar.width - height: _isVertical ? scrollbar.width : scrollbar.height - rotation: _isVertical ? -90 : 0 - - anchors.centerIn: parent - PlasmaCore.Svg { - id: scrollbarSvg - imagePath: "widgets/scrollbar" + 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)) + } } - PlasmaCore.SvgItem { - id: leftButton + RangeModel { + id: range - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - height: 18 - width: _showButtons ? 18 : 0 - svg: scrollbarSvg - elementId: { - if (leftMousArea.pressed) - return "sunken-arrow-left"; + minimumValue: 0 + maximumValue: { + var diff; + if (_isVertical) { + diff = flickableItem.contentHeight - flickableItem.height + } else { + diff = flickableItem.contentWidth - flickableItem.width + } - if (scrollbar.activeFocus || leftMousArea.containsMouse) - return "mouseover-arrow-left"; - else - return "arrow-left"; + return Math.max(0, diff) } - MouseArea { - id: leftMousArea + 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 + } - anchors.fill: parent - enabled: scrollbar.enabled - Timer { - id: leftTimer - interval: scrollbar.scrollButtonInterval; - running: parent.pressed - repeat: true - onTriggered: { - scrollbar.forceActiveFocus(); - if (_inverted) - incrementValue(stepSize); - else - incrementValue(-stepSize); - } + 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 } } } - PlasmaCore.SvgItem { - id: rightButton - - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - } - height: 18 - width: _showButtons ? 18 : 0 - svg: scrollbarSvg - elementId: { - if (rightMousArea.pressed) - return "sunken-arrow-right"; - - if (scrollbar.activeFocus || rightMousArea.containsMouse) - return "mouseover-arrow-right"; - else - return "arrow-right"; - } - - MouseArea { - id: rightMousArea - - anchors.fill: parent - enabled: scrollbar.enabled - Timer { - id: rightTimer - interval: scrollbar.scrollButtonInterval; - running: parent.pressed; - repeat: true - onTriggered: { - scrollbar.forceActiveFocus(); - if (_inverted) - incrementValue(-stepSize); - else - 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; } - } - + sourceComponent: Component { PlasmaCore.FrameSvgItem { - id: groove - + id: background anchors.fill: parent - imagePath: "widgets/scrollbar" - prefix: "background-horizontal" - } + imagePath:"widgets/scrollbar" + prefix: _isVertical ? "background-vertical" : "background-horizontal" - PlasmaCore.FrameSvgItem { - id: handle + property Item handle: 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; + 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 + } - return ratio * parent.width; - } - height: parent.height - margins.top // TODO: check mergin - imagePath: "widgets/scrollbar" - prefix: { - if (scrollbar.pressed) - return "sunken-slider"; + 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"; + 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 + } } - Behavior on x { - id: behavior - enabled: !mouseArea.drag.active && scrollbar.animated && - !flickableItem.flicking + PlasmaCore.Svg { + id: scrollbarSvg + imagePath: "widgets/scrollbar" + } - PropertyAnimation { - duration: behavior.enabled ? 150 : 0 - easing.type: Easing.OutSine + PlasmaCore.SvgItem { + id: leftButton + visible: stepSize > 0 + + anchors { + 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 (leftMouseArea.pressed) { + return _isVertical ? "sunken-arrow-up" : "sunken-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: leftMouseArea + + anchors.fill: parent + enabled: scrollbar.enabled + Timer { + id: leftTimer + interval: scrollbar.scrollButtonInterval; + running: parent.pressed + repeat: true + onTriggered: { + scrollbar.forceActiveFocus() + if (inverted) { + internalLoader.incrementValue(stepSize); + } else { + internalLoader.incrementValue(-stepSize); + } + } + } + } + } + + PlasmaCore.SvgItem { + id: rightButton + visible: stepSize > 0 + + anchors { + 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 (leftMouseArea.pressed) { + return _isVertical ? "sunken-arrow-down" : "sunken-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: rightMouseArea + + anchors.fill: parent + enabled: scrollbar.enabled + Timer { + id: rightTimer + interval: scrollbar.scrollButtonInterval; + running: parent.pressed; + repeat: true + onTriggered: { + scrollbar.forceActiveFocus(); + if (inverted) + internalLoader.incrementValue(-stepSize); + else + internalLoader.incrementValue(stepSize); + } + } + } + } + + property MouseArea mouseArea: mouseArea + MouseArea { + id: mouseArea + + anchors.fill: contents + enabled: scrollbar.enabled + hoverEnabled: true + drag { + 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 (newX > handle.x + handle.width) { + handle.x = mouse.x - handle.width + } else if (newX < handle.x) { + handle.x = mouse.x + } + } + + scrollbar.forceActiveFocus(); } } } - - Item { - id: fakeHandle - width: handle.width - height: handle.height - transform: Translate { x: - handle.width / 2 } - } - - MouseArea { - id: mouseArea - - anchors.fill: parent - enabled: scrollbar.enabled - drag { - target: fakeHandle - axis: Drag.XAxis - minimumX: range.positionAtMinimum - maximumX: range.positionAtMaximum - } - - onPressed: { - // 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; - - 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 } } }