components for android added
[mardrone] / mardrone / imports / com / nokia / meego / SliderTemplate.qml
diff --git a/mardrone/imports/com/nokia/meego/SliderTemplate.qml b/mardrone/imports/com/nokia/meego/SliderTemplate.qml
new file mode 100644 (file)
index 0000000..05d79a4
--- /dev/null
@@ -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
+    }
+}