X-Git-Url: http://git.maemo.org/git/?p=mardrone;a=blobdiff_plain;f=mardrone%2Fimports%2Fcom%2Fnokia%2Fmeego%2FSliderTemplate.qml;fp=mardrone%2Fimports%2Fcom%2Fnokia%2Fmeego%2FSliderTemplate.qml;h=05d79a4922fd7c5a33b66823656c06a176405128;hp=0000000000000000000000000000000000000000;hb=53abfcee86d03676ae5e3b648d6c7cee69c2f4e1;hpb=f7f1d7c4427cba2c8490f2767259b1d06f66c7ed diff --git a/mardrone/imports/com/nokia/meego/SliderTemplate.qml b/mardrone/imports/com/nokia/meego/SliderTemplate.qml new file mode 100644 index 0000000..05d79a4 --- /dev/null +++ b/mardrone/imports/com/nokia/meego/SliderTemplate.qml @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import Qt.labs.components 1.1 + +Item { + id: slider + + // + // Common API + // + property int orientation: Qt.Horizontal + property alias minimumValue: range.minimumValue + property alias maximumValue: range.maximumValue + property alias pressed: mouseArea.pressed + property alias stepSize: range.stepSize + property alias platformMouseAnchors: mouseArea.anchors + + // NOTE: this property is in/out, the user can set it, create bindings to it, and + // at the same time the slider wants to update. There's no way in QML to do this kind + // of updates AND allow the user bind it (without a Binding object). That's the + // reason this is an alias to a C++ property in range model. + property alias value: range.value + + // + // Public extensions + // + property alias inverted: range.inverted + + // Value indicator displays the current value near the slider + // if valueIndicatorText == "", a default formating will be applied + property string valueIndicatorText: formatValue(range.value) + property bool valueIndicatorVisible: false + + property int valueIndicatorMargin: 1 + property string valueIndicatorPosition: __isVertical ? "Left" : "Top" + + // The default implementation for label hides decimals until it hits a + // floating point value at which point it keeps decimals + property bool __useDecimals: false + function formatValue(v) { + return __useDecimals ? (v.toFixed(2)) : v; + } + + // + // "Protected" properties + // + + // Hooks for customizing the pieces of the slider + property Item __grooveItem + property Item __valueTrackItem + property Item __handleItem + property Item __valueIndicatorItem + + property bool __isVertical: orientation == Qt.Vertical + + implicitWidth: 400 + implicitHeight: handle.height + + width: __isVertical ? implicitHeight : implicitWidth + height: __isVertical ? implicitWidth : implicitHeight + + // This is a template slider, so every piece can be modified by passing a + // different Component. The main elements in the implementation are + // + // - the 'range' does the calculations to map position to/from value, + // it also serves as a data storage for both properties; + // + // - the 'fakeHandle' is what the mouse area drags on the screen, it feeds + // the 'range' position and also reads it when convenient; + // + // - the real 'handle' it is the visual representation of the handle, that + // just follows the 'fakeHandle' position. + // + // Everything is encapsulated in a contents Item, so for the + // vertical slider, we just swap the height/width, make it + // horizontal, and then use rotation to make it vertical again. + + Component.onCompleted: { + __grooveItem.parent = groove; + __valueTrackItem.parent = valueTrack; + __handleItem.parent = handle; + __valueIndicatorItem.parent = valueIndicator; + } + + Item { + id: contents + + width: __isVertical ? slider.height : slider.width + height: __isVertical ? slider.width : slider.height + rotation: __isVertical ? -90 : 0 + + anchors.centerIn: slider + + RangeModel { + id: range + minimumValue: 0.0 + maximumValue: 1.0 + value: 0 + stepSize: 0.0 + onValueChanged: { + // XXX: Moved that outside formatValue to get rid of binding loop warnings + var v = range.value + if (parseInt(v) != v) + __useDecimals = true; + } + positionAtMinimum: handle.width / 2 + positionAtMaximum: contents.width - handle.width / 2 + onMaximumChanged: __useDecimals = false; + onMinimumChanged: __useDecimals = false; + onStepSizeChanged: __useDecimals = false; + } + + Item { + id: groove + anchors.fill: parent + anchors.leftMargin: handle.width / 2 + anchors.rightMargin: handle.width / 2 + } + + Item { + id: valueTrack + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: groove.left + anchors.right: handle.horizontalCenter + anchors.rightMargin: handle.width / 2 + + states: State { + when: slider.inverted + PropertyChanges { + target: valueTrack + anchors.rightMargin: 0 + anchors.leftMargin: - handle.width / 2 + } + AnchorChanges { + target: valueTrack + anchors.left: handle.horizontalCenter + anchors.right: groove.right + } + } + } + + Item { + id: handle + transform: Translate { x: - handle.width / 2 } + rotation: __isVertical ? 90 : 0 + + anchors.verticalCenter: parent.verticalCenter + + width: __handleItem.width + height: __handleItem.height + + x: fakeHandle.x + Behavior on x { + id: behavior + enabled: !mouseArea.drag.active + + 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 } + } + + MouseArea { + id: mouseArea + property real oldPosition: 0 + anchors { + fill: parent + rightMargin: platformStyle.mouseMarginRight + leftMargin: platformStyle.mouseMarginLeft + topMargin: platformStyle.mouseMarginTop + bottomMargin: platformStyle.mouseMarginBottom + } + + drag.target: fakeHandle + drag.axis: Drag.XAxis + drag.minimumX: range.positionAtMinimum + drag.maximumX: range.positionAtMaximum + + onPressed: { + oldPosition = range.position; + // Clamp the value + var newX = Math.max(mouse.x + anchors.leftMargin, 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; + } + + onCanceled: { + range.position = oldPosition; + } + } + + Item { + id: valueIndicator + + transform: Translate { + x: - handle.width / 2; + y: __isVertical? -(__valueIndicatorItem.width/2)+20 : y ; + } + + rotation: __isVertical ? 90 : 0 + visible: valueIndicatorVisible + + width: __valueIndicatorItem.width //+ (__isVertical? (handle.width/2) : 0 ) + height: __valueIndicatorItem.height + + state: { + if (!__isVertical) + return slider.valueIndicatorPosition; + + if (valueIndicatorPosition == "Right") + return "Bottom"; + if (valueIndicatorPosition == "Top") + return "Right"; + if (valueIndicatorPosition == "Bottom") + return "Left"; + + return "Top"; + } + + anchors.margins: valueIndicatorMargin + + states: [ + State { + name: "Top" + AnchorChanges { + target: valueIndicator + anchors.bottom: handle.top + anchors.horizontalCenter: handle.horizontalCenter + } + }, + State { + name: "Bottom" + AnchorChanges { + target: valueIndicator + anchors.top: handle.bottom + anchors.horizontalCenter: handle.horizontalCenter + } + }, + State { + name: "Right" + AnchorChanges { + target: valueIndicator + anchors.left: handle.right + anchors.verticalCenter: handle.verticalCenter + } + }, + State { + name: "Left" + AnchorChanges { + target: valueIndicator + anchors.right: handle.left + anchors.verticalCenter: handle.verticalCenter + } + } + ] + } + } + + // when there is no mouse interaction, the handle's position binds to the value + Binding { + when: !mouseArea.drag.active + target: fakeHandle + property: "x" + value: range.position + } + + // when the slider is dragged, the value binds to the handle's position + Binding { + when: mouseArea.drag.active + target: range + property: "position" + value: fakeHandle.x + } +}