added android components
[mardrone] / mardrone / imports / com / nokia / android.1.1 / ScrollBar.qml
diff --git a/mardrone/imports/com/nokia/android.1.1/ScrollBar.qml b/mardrone/imports/com/nokia/android.1.1/ScrollBar.qml
new file mode 100644 (file)
index 0000000..e356c55
--- /dev/null
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** 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 "SectionScroller.js" as Sections
+import "." 1.1
+
+Item {
+    id: root
+
+    property Flickable flickableItem: null
+    property int orientation: Qt.Vertical
+    property bool interactive: true
+    property int policy: Android.ScrollBarWhenScrolling
+    property bool privateSectionScroller: false
+    property bool platformInverted: false
+
+    //implicit values for qml designer when no Flickable is present
+    implicitHeight: privateStyle.scrollBarThickness * (orientation == Qt.Vertical ? 3 : 1)
+    implicitWidth: privateStyle.scrollBarThickness * (orientation == Qt.Horizontal ? 3 : 1)
+    height: {
+        if (flickableItem && orientation == Qt.Vertical)
+            return Math.floor(Math.round(flickableItem.height) - anchors.topMargin - anchors.bottomMargin)
+        return undefined
+    }
+    width: {
+        if (flickableItem && orientation == Qt.Horizontal)
+            return Math.floor(Math.round(flickableItem.width) - anchors.leftMargin - anchors.rightMargin)
+        return undefined
+    }
+    opacity: internal.rootOpacity
+
+    onFlickableItemChanged: internal.initSectionScroller();
+    onPrivateSectionScrollerChanged: internal.initSectionScroller();
+
+    //For showing explicitly a ScrollBar if policy is Android.ScrollBarWhenScrolling
+    function flash(type) {
+        if (policy == Android.ScrollBarWhenScrolling && internal.scrollBarNeeded) {
+            flashEffect.type = (type == undefined) ? Android.FadeOut : type
+            flashEffect.restart()
+        }
+    }
+
+    Connections {
+        target: screen
+        onCurrentOrientationChanged: flash()
+    }
+
+    Connections {
+        target: root.privateSectionScroller ? flickableItem : null
+        onModelChanged: internal.initSectionScroller()
+    }
+
+    Loader {
+        id: listItemInteraction
+        sourceComponent: flickableItem && flickableItem.hasOwnProperty("currentIndex") && flickableItem.activeFocus ? listItemInteractionComponent : undefined
+    }
+
+    Component {
+        id: listItemInteractionComponent
+        Item {
+            Connections {
+                target: symbian
+                onListInteractionModeChanged: flash()
+                onPrivateListItemKeyNavigation: flash()
+            }
+        }
+    }
+
+    QtObject {
+        id: internal
+        property int hideTimeout: 500
+        property int pageStepY: flickableItem ? Math.floor(flickableItem.visibleArea.heightRatio * flickableItem.contentHeight) : NaN
+        property int pageStepX: flickableItem ? Math.floor(flickableItem.visibleArea.widthRatio * flickableItem.contentWidth) : NaN
+        property int handleY: flickableItem ? Math.floor(handle.y / flickableItem.height * flickableItem.contentHeight) : NaN
+        property int handleX: flickableItem ? Math.floor(handle.x / flickableItem.width * flickableItem.contentWidth) : NaN
+        property int maximumY: flickableItem ? Math.floor(Math.min(flickableItem.contentHeight - root.height, flickableItem.contentY)) : NaN
+        property int maximumX: flickableItem ? Math.floor(Math.min(flickableItem.contentWidth - root.width, flickableItem.contentX)) : NaN
+        property bool scrollBarNeeded: root.visible && hasScrollableContent()
+        //Sets currentSection to empty string when flickableItem.currentSection is null
+        property string currentSection: flickableItem ? flickableItem.currentSection || "" : ""
+        //To be able to pressed on trackMouseArea, opacity needs to be greater than 0
+        property real rootOpacity: root.privateSectionScroller ? 0.01 : 0
+
+        // The following properties are used for calculating handle position and size:
+        // Minimum allowed handle length
+        property real minHandleLength: 3 * Math.floor(privateStyle.scrollBarThickness)
+        // Scroll bar "container" length
+        property real totalLength: !flickableItem ? NaN : (orientation == Qt.Vertical ? root.height : root.width)
+        property real relativeHandlePosition: !flickableItem ? NaN : (orientation == Qt.Vertical
+                                                                      ? flickableItem.visibleArea.yPosition
+                                                                      : flickableItem.visibleArea.xPosition)
+        property real lengthRatio: !flickableItem ? NaN : (orientation == Qt.Vertical
+                                                           ? flickableItem.visibleArea.heightRatio
+                                                           : flickableItem.visibleArea.widthRatio)
+        // Scroll bar handle length under normal circumstances
+        property real staticHandleLength: Math.max(minHandleLength, lengthRatio * totalLength)
+        // Adjustment factor needed to calculate correct position, which is needed because
+        // relativeHandlePosition and lengthRatio are assuming default handle length. Dividend is
+        // the maximum handle position taking into account restricted minimum handle length, whereas
+        // divisor is the maximum handle position if default handle size would be used.
+        property real handlePositionAdjustment: (totalLength - staticHandleLength) / (totalLength * (1 - lengthRatio))
+        property real handlePosition
+        handlePosition: {
+            // handle's position, which is adjusted to take into account non-default handle
+            // length and restricted never to exceed flickable boundaries
+            var pos = totalLength * relativeHandlePosition * handlePositionAdjustment
+            return Math.min(Math.max(0, pos), totalLength - minHandleLength)
+        }
+        property real dynamicHandleLength
+        dynamicHandleLength: {
+            // dynamic handle length, which may differ from static due to bounds behavior, i.e.
+            // handle gets shorter when content exceeds flickable boundaries
+            var length = 0
+            if (relativeHandlePosition >= 1 - lengthRatio) // overflow
+                length = 1 - relativeHandlePosition
+            else if (relativeHandlePosition < 0) // underflow
+                length = lengthRatio + relativeHandlePosition
+            else
+                length = lengthRatio
+            return Math.max(minHandleLength, length * totalLength)
+        }
+
+        // Show flash effect in case if no flicking (flickableItem.moving) has occured
+        // but there is a need to indicate scrollable content.
+        onScrollBarNeededChanged: {
+            if (stateGroup.state == "") {
+                if (scrollBarNeeded)
+                    flash()
+                else
+                    idleEffect.restart()
+            }
+        }
+
+        /**
+         * Checks whether ScrollBar is needed or not
+         * based on Flickable visibleArea height and width ratios
+         */
+        function hasScrollableContent() {
+            if (!flickableItem)
+                return false
+            var ratio = orientation == Qt.Vertical ? flickableItem.visibleArea.heightRatio : flickableItem.visibleArea.widthRatio
+            return ratio < 1.0 && ratio > 0
+        }
+        /**
+         * Does Page by Page movement of flickableItem
+         * when ScrollBar Track is being clicked/pressed
+         *
+         * @see #moveToLongTapPosition
+         */
+        function doPageStep() {
+            if (orientation == Qt.Vertical) {
+                if (trackMouseArea.mouseY > (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY += pageStepY
+                    flickableItem.contentY = maximumY
+                }
+                else if (trackMouseArea.mouseY < (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY -= pageStepY
+                    flickableItem.contentY = Math.max(0, flickableItem.contentY)
+                }
+            } else {
+                if (trackMouseArea.mouseX > (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX += pageStepX
+                    flickableItem.contentX = maximumX
+                }
+                else if (trackMouseArea.mouseX < (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX -= pageStepX
+                    flickableItem.contentX = Math.max(0, flickableItem.contentX)
+                }
+            }
+        }
+        /**
+         * Does movement of flickableItem
+         * when ScrollBar Handle is being dragged
+         */
+        function moveToHandlePosition() {
+            if (orientation == Qt.Vertical)
+                flickableItem.contentY = handleY
+            else
+                flickableItem.contentX = handleX
+        }
+        /**
+         * Moves flickableItem's content according to given mouseArea movement
+         * when mouseArea is pressed long
+         * Tries to position the handle and content in center of mouse position enough
+         *
+         * @see #doPageStep
+         */
+        function moveToLongTapPosition(mouseArea) {
+            if (orientation == Qt.Vertical) {
+                if (Math.abs(mouseArea.mouseY - (handle.height / 2 + handle.y)) < privateStyle.scrollBarThickness)
+                    return //if change is not remarkable enough, do nothing otherwise it would cause annoying flickering effect
+                if (mouseArea.mouseY > (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY += Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentY = maximumY
+                }
+                else if (mouseArea.mouseY < (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY -= Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentY = Math.floor(Math.max(0, flickableItem.contentY))
+                }
+            } else {
+                if (Math.abs(mouseArea.mouseX - (handle.width / 2 + handle.x)) < privateStyle.scrollBarThickness)
+                    return //if change is not remarkable enough, do nothing otherwise it would cause annoying flickering effect
+                if (mouseArea.mouseX > (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX += Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentX = maximumX
+                }
+                else if (mouseArea.mouseX < (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX -= Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentX = Math.floor(Math.max(0, flickableItem.contentX))
+                }
+            }
+        }
+
+        function adjustContentPosition(y) {
+            if (y < 0 || y > trackMouseArea.height)
+                return;
+
+            var sect = Sections.closestSection(y / trackMouseArea.height);
+            var idx = Sections.indexOf(sect);
+            currentSection = sect;
+            flickableItem.positionViewAtIndex(idx, ListView.Beginning);
+        }
+
+        function initSectionScroller() {
+            if (root.privateSectionScroller && flickableItem && flickableItem.model)
+                Sections.initSectionData(flickableItem);
+        }
+    }
+
+    BorderImage {
+        id: track
+        objectName: "track"
+        source: privateStyle.imagePath((orientation == Qt.Vertical
+                                        ? "qtg_fr_scrollbar_v_track_normal"
+                                        : "qtg_fr_scrollbar_h_track_normal"),
+                                       root.platformInverted)
+        visible: interactive
+        anchors.fill: parent
+        border.right: orientation == Qt.Horizontal ? 7 : 0
+        border.left: orientation == Qt.Horizontal ? 7 : 0
+        border.top: orientation == Qt.Vertical ? 7 : 0
+        border.bottom: orientation == Qt.Vertical ? 7 : 0
+        onVisibleChanged: { idleEffect.complete(); flashEffect.complete() }
+    }
+
+    Loader {
+        id: sectionScrollBackground
+        anchors.right: trackMouseArea.right
+        width: flickableItem ? flickableItem.width : 0
+        height: platformStyle.fontSizeMedium * 5
+        sourceComponent: root.privateSectionScroller ? sectionScrollComponent : null
+    }
+
+    Component {
+        id: sectionScrollComponent
+        BorderImage {
+            id: indexFeedbackBackground
+            objectName: "indexFeedbackBackground"
+            source: privateStyle.imagePath("qtg_fr_popup_transparent", root.platformInverted)
+            border { left: platformStyle.borderSizeMedium; top: platformStyle.borderSizeMedium; right: platformStyle.borderSizeMedium; bottom: platformStyle.borderSizeMedium }
+            visible: trackMouseArea.pressed
+            anchors.fill: parent
+            Text {
+                id: indexFeedbackText
+                objectName: "indexFeedbackText"
+                color: root.platformInverted ? platformStyle.colorNormalDarkInverted // intentionally dark inverted
+                                             : platformStyle.colorNormalLight
+                anchors {
+                    left: parent.left;
+                    leftMargin: platformStyle.paddingLarge;
+                    right: parent.right;
+                    rightMargin: platformStyle.paddingLarge;
+                    verticalCenter: parent.verticalCenter
+                }
+                font {
+                    family: platformStyle.fontFamilyRegular;
+                    pixelSize: indexFeedbackText.text.length == 1 ? platformStyle.fontSizeMedium * 4 : platformStyle.fontSizeMedium * 2;
+                    capitalization: indexFeedbackText.text.length == 1 ? Font.AllUppercase : Font.MixedCase
+                }
+                text: internal.currentSection
+                horizontalAlignment: Text.AlignLeft
+                elide: Text.ElideRight
+            }
+            states: [
+                State {
+                    when: (handle.y + (handle.height / 2)) - (sectionScrollBackground.height / 2) < 0
+                    AnchorChanges {
+                        target: sectionScrollBackground
+                        anchors { verticalCenter: undefined; top: track.top; bottom: undefined }
+                    }
+                },
+                State {
+                    when: (handle.y + (handle.height / 2)) + (sectionScrollBackground.height / 2) >= track.height
+                    AnchorChanges {
+                        target: sectionScrollBackground
+                        anchors { verticalCenter: undefined; top: undefined; bottom: track.bottom }
+                    }
+                },
+                State {
+                    when: (handle.y + (handle.height / 2)) - (sectionScrollBackground.height / 2) >= 0
+                    AnchorChanges {
+                        target: sectionScrollBackground
+                        anchors { verticalCenter: handle.verticalCenter; top: undefined; bottom: undefined }
+                    }
+                }
+            ]
+        }
+    }
+
+    // MouseArea for the move content "page by page" by tapping and scroll to press-and-hold position
+    MouseArea {
+        id: trackMouseArea
+        objectName: "trackMouseArea"
+        property bool longPressed: false
+        enabled: root.privateSectionScroller || interactive
+        anchors {
+            top: root.privateSectionScroller ? parent.top : undefined;
+            bottom: root.privateSectionScroller ? parent.bottom : undefined;
+            right: root.privateSectionScroller ? parent.right : undefined;
+            fill: root.privateSectionScroller ? undefined : (flickableItem ? track : undefined)
+        }
+        width: root.privateSectionScroller ? privateStyle.scrollBarThickness * 3 : undefined
+        drag {
+            target: root.privateSectionScroller ? sectionScrollBackground : undefined
+            // axis is set XandY to prevent flickable from stealing the mouse event
+            // SectionScroller is anchored to the right side of the mouse area so the user
+            // won't be able to drag it along the X axis
+            axis: root.privateSectionScroller ? Drag.XandYAxis : 0
+            minimumY: root.privateSectionScroller ? (flickableItem ? flickableItem.y : 0) : 0
+            maximumY: root.privateSectionScroller ? (flickableItem ? (trackMouseArea.height - sectionScrollBackground.height) : 0) : 0
+        }
+
+        onPressAndHold: {
+            if (!root.privateSectionScroller)
+                longPressed = true
+        }
+
+        onReleased: longPressed = false
+
+        onPositionChanged: {
+            if (root.privateSectionScroller)
+                internal.adjustContentPosition(trackMouseArea.mouseY);
+        }
+
+        onPressedChanged: {
+            if (root.privateSectionScroller && trackMouseArea.pressed)
+                internal.adjustContentPosition(trackMouseArea.mouseY);
+        }
+    }
+    Timer {
+        id: pressAndHoldTimer
+        running: trackMouseArea.longPressed
+        interval: 50
+        repeat: true
+        onTriggered: { internal.moveToLongTapPosition(trackMouseArea); privateStyle.play(Android.SensitiveSlider) }
+    }
+
+    BorderImage {
+        id: handle
+        objectName: "handle"
+        source: privateStyle.imagePath(handleFileName(), root.platformInverted)
+        x: orientation == Qt.Horizontal ? internal.handlePosition : NaN
+        y: orientation == Qt.Vertical ? internal.handlePosition : NaN
+        height: orientation == Qt.Vertical ? internal.dynamicHandleLength : root.height
+        width: orientation == Qt.Horizontal ? internal.dynamicHandleLength : root.width
+        border.right: orientation == Qt.Horizontal ? 7 : 0
+        border.left: orientation == Qt.Horizontal ? 7 : 0
+        border.top: orientation == Qt.Vertical ? 7 : 0
+        border.bottom: orientation == Qt.Vertical ? 7 : 0
+
+        function handleFileName() {
+            var fileName = (orientation == Qt.Vertical ? "qtg_fr_scrollbar_v_handle_" :
+                            "qtg_fr_scrollbar_h_handle_")
+            if (!interactive)
+                fileName += "indicative"
+            else if (handleMouseArea.pressed)
+                fileName += "pressed"
+            else
+                fileName += "normal"
+            return fileName
+        }
+    }
+
+    MouseArea {
+        id: handleMouseArea
+        objectName: "handleMouseArea"
+        property real maxDragY: flickableItem ? flickableItem.height - handle.height - root.anchors.topMargin - root.anchors.bottomMargin : NaN
+        property real maxDragX: flickableItem ? flickableItem.width - handle.width - root.anchors.leftMargin - root.anchors.rightMargin : NaN
+        enabled: interactive && !root.privateSectionScroller
+        width: orientation == Qt.Vertical ? 3 * privateStyle.scrollBarThickness : handle.width
+        height: orientation == Qt.Horizontal ? 3 * privateStyle.scrollBarThickness : handle.height
+        anchors {
+            verticalCenter: flickableItem ? handle.verticalCenter : undefined
+            horizontalCenter: flickableItem ? handle.horizontalCenter : undefined
+        }
+        drag {
+            target: handle
+            axis: orientation == Qt.Vertical ? Drag.YAxis : Drag.XAxis
+            minimumY: 0
+            maximumY: maxDragY
+            minimumX: 0
+            maximumX: maxDragX
+        }
+        onPositionChanged: internal.moveToHandlePosition()
+    }
+
+    PropertyAnimation {
+        id: indicateEffect
+
+        function play() {
+            flashEffect.stop()
+            idleEffect.stop()
+            restart()
+        }
+
+        target: root
+        property: "opacity"
+        to: 1
+        duration: 0
+    }
+    SequentialAnimation {
+        id: idleEffect
+
+        function play() {
+            indicateEffect.stop()
+            if (internal.scrollBarNeeded && root.policy == Android.ScrollBarWhenScrolling)
+                restart()
+        }
+
+        PauseAnimation { duration: root.interactive ? 1500 : 0 }
+        PropertyAnimation {
+            target: root
+            property: "opacity"
+            to: internal.rootOpacity
+            duration: internal.hideTimeout
+        }
+    }
+    SequentialAnimation {
+        id: flashEffect
+        property int type: Android.FadeOut
+
+        PropertyAnimation {
+            target: root
+            property: "opacity"
+            to: 1
+            duration: (flashEffect.type == Android.FadeInFadeOut) ? internal.hideTimeout : 0
+        }
+        PropertyAnimation {
+            target: root
+            property: "opacity"
+            to: internal.rootOpacity
+            duration: internal.hideTimeout
+        }
+    }
+    StateGroup {
+        id: stateGroup
+        states: [
+            State {
+                name: "Move"
+                when: handleMouseArea.pressed
+            },
+            State {
+                name: "Stepping"
+                when: trackMouseArea.longPressed
+            },
+            State {
+                name: "Step"
+                when: trackMouseArea.pressed && !trackMouseArea.longPressed && !root.privateSectionScroller
+            },
+            State {
+                name: "Indicate"
+                when: (internal.scrollBarNeeded && flickableItem.moving) ||
+                      (trackMouseArea.pressed && root.privateSectionScroller)
+            },
+            State {
+                name: ""
+            }
+        ]
+        transitions: [
+            Transition {
+                to: "Move"
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                to: "Step"
+                ScriptAction { script: internal.doPageStep() }
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                from: "Step"
+                to: "Stepping"
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                from: "Move"
+                to: "Indicate"
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                to: "Indicate"
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                from: "Move"
+                to: ""
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: idleEffect.play() }
+            },
+            Transition {
+                to: ""
+                ScriptAction { script: idleEffect.play() }
+            }
+        ]
+    }
+}