libandroidplugin added
[mardrone] / mardrone / imports / Qt / labs / components / native / SliderTemplate.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Components project.
8 **
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
11 **
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
14 ** met:
15 **   * Redistributions of source code must retain the above copyright
16 **     notice, this list of conditions and the following disclaimer.
17 **   * Redistributions in binary form must reproduce the above copyright
18 **     notice, this list of conditions and the following disclaimer in
19 **     the documentation and/or other materials provided with the
20 **     distribution.
21 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 **     the names of its contributors may be used to endorse or promote
23 **     products derived from this software without specific prior written
24 **     permission.
25 **
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 import QtQuick 1.1
42 import Qt.labs.components 1.1
43
44 Item {
45     id: slider
46
47     //
48     // Common API
49     //
50     property int orientation: Qt.Horizontal
51     property alias minimumValue: range.minimumValue
52     property alias maximumValue: range.maximumValue
53     property alias pressed: mouseArea.pressed
54     property alias stepSize: range.stepSize
55     property alias platformMouseAnchors: mouseArea.anchors
56
57     // NOTE: this property is in/out, the user can set it, create bindings to it, and
58     // at the same time the slider wants to update. There's no way in QML to do this kind
59     // of updates AND allow the user bind it (without a Binding object). That's the
60     // reason this is an alias to a C++ property in range model.
61     property alias value: range.value
62
63     //
64     // Public extensions
65     //
66     property alias inverted: range.inverted
67
68     // Value indicator displays the current value near the slider
69     // if valueIndicatorText == "", a default formating will be applied
70     property string valueIndicatorText: formatValue(range.value)
71     property bool valueIndicatorVisible: false
72
73     property int valueIndicatorMargin: 1
74     property string valueIndicatorPosition: __isVertical ? "Left" : "Top"
75
76     // The default implementation for label hides decimals until it hits a
77     // floating point value at which point it keeps decimals
78     property bool __useDecimals: false
79     function formatValue(v) {
80         return __useDecimals ? (v.toFixed(2)) : v;
81     }
82
83     //
84     // "Protected" properties
85     //
86
87     // Hooks for customizing the pieces of the slider
88     property Item __grooveItem
89     property Item __valueTrackItem
90     property Item __handleItem
91     property Item __valueIndicatorItem
92
93     property bool __isVertical: orientation == Qt.Vertical
94
95     implicitWidth: 400
96     implicitHeight: handle.height
97
98     width: __isVertical ? implicitHeight : implicitWidth
99     height: __isVertical ? implicitWidth : implicitHeight
100
101     // This is a template slider, so every piece can be modified by passing a
102     // different Component. The main elements in the implementation are
103     //
104     // - the 'range' does the calculations to map position to/from value,
105     //   it also serves as a data storage for both properties;
106     //
107     // - the 'fakeHandle' is what the mouse area drags on the screen, it feeds
108     //   the 'range' position and also reads it when convenient;
109     //
110     // - the real 'handle' it is the visual representation of the handle, that
111     //   just follows the 'fakeHandle' position.
112     //
113     // Everything is encapsulated in a contents Item, so for the
114     // vertical slider, we just swap the height/width, make it
115     // horizontal, and then use rotation to make it vertical again.
116
117     Component.onCompleted: {
118         __grooveItem.parent = groove;
119         __valueTrackItem.parent = valueTrack;
120         __handleItem.parent = handle;
121         __valueIndicatorItem.parent = valueIndicator;
122     }
123
124     Item {
125         id: contents
126
127         width: __isVertical ? slider.height : slider.width
128         height: __isVertical ? slider.width : slider.height
129         rotation: __isVertical ? -90 : 0
130
131         anchors.centerIn: slider
132
133         RangeModel {
134             id: range
135             minimumValue: 0.0
136             maximumValue: 1.0
137             value: 0
138             stepSize: 0.0
139             onValueChanged: {
140                 // XXX: Moved that outside formatValue to get rid of binding loop warnings
141                 var v = range.value
142                 if (parseInt(v) != v)
143                     __useDecimals = true;
144             }
145             positionAtMinimum: handle.width / 2
146             positionAtMaximum: contents.width - handle.width / 2
147             onMaximumChanged: __useDecimals = false;
148             onMinimumChanged: __useDecimals = false;
149             onStepSizeChanged: __useDecimals = false;
150         }
151
152         Item {
153             id: groove
154             anchors.fill: parent
155             anchors.leftMargin: handle.width / 2
156             anchors.rightMargin: handle.width / 2
157         }
158
159         Item {
160             id: valueTrack
161
162             anchors.top: parent.top
163             anchors.bottom: parent.bottom
164             anchors.left: groove.left
165             anchors.right: handle.horizontalCenter
166             anchors.rightMargin: handle.width / 2
167
168             states: State {
169                 when: slider.inverted
170                 PropertyChanges {
171                     target: valueTrack
172                     anchors.rightMargin: 0
173                     anchors.leftMargin: - handle.width / 2
174                 }
175                 AnchorChanges {
176                     target: valueTrack
177                     anchors.left: handle.horizontalCenter
178                     anchors.right: groove.right
179                 }
180             }
181         }
182
183         Item {
184             id: handle
185             transform: Translate { x: - handle.width / 2 }
186             rotation: __isVertical ? 90 : 0
187
188             anchors.verticalCenter: parent.verticalCenter
189
190             width: __handleItem.width
191             height: __handleItem.height
192
193             x: fakeHandle.x
194             Behavior on x {
195                 id: behavior
196                 enabled: !mouseArea.drag.active
197
198                 PropertyAnimation {
199                     duration: behavior.enabled ? 150 : 0
200                     easing.type: Easing.OutSine
201                 }
202             }
203         }
204
205         Item {
206             id: fakeHandle
207             width: handle.width
208             height: handle.height
209             transform: Translate { x: - handle.width / 2 }
210         }
211
212         MouseArea {
213             id: mouseArea
214             property real oldPosition: 0
215             anchors {
216                 fill: parent
217                 rightMargin: platformStyle.mouseMarginRight
218                 leftMargin: platformStyle.mouseMarginLeft
219                 topMargin: platformStyle.mouseMarginTop
220                 bottomMargin: platformStyle.mouseMarginBottom
221             }
222
223             drag.target: fakeHandle
224             drag.axis: Drag.XAxis
225             drag.minimumX: range.positionAtMinimum
226             drag.maximumX: range.positionAtMaximum
227
228             onPressed: {
229                 oldPosition = range.position;
230                 // Clamp the value
231                 var newX = Math.max(mouse.x + anchors.leftMargin, drag.minimumX);
232                 newX = Math.min(newX, drag.maximumX);
233
234                 // Debounce the press: a press event inside the handler will not
235                 // change its position, the user needs to drag it.
236                 if (Math.abs(newX - fakeHandle.x) > handle.width / 2)
237                     range.position = newX;
238             }
239
240             onCanceled: {
241                 range.position = oldPosition;
242             }
243         }
244
245         Item {
246             id: valueIndicator
247
248             transform: Translate {
249                 x: - handle.width / 2;
250                 y: __isVertical? -(__valueIndicatorItem.width/2)+20 : y ;
251             }
252
253             rotation: __isVertical ? 90 : 0
254             visible: valueIndicatorVisible
255
256             width: __valueIndicatorItem.width //+ (__isVertical? (handle.width/2) : 0 )
257             height: __valueIndicatorItem.height
258
259             state: {
260                 if (!__isVertical)
261                     return slider.valueIndicatorPosition;
262
263                 if (valueIndicatorPosition == "Right")
264                     return "Bottom";
265                 if (valueIndicatorPosition == "Top")
266                     return "Right";
267                 if (valueIndicatorPosition == "Bottom")
268                     return "Left";
269
270                 return "Top";
271             }
272
273             anchors.margins: valueIndicatorMargin
274
275             states: [
276                 State {
277                     name: "Top"
278                     AnchorChanges {
279                         target: valueIndicator
280                         anchors.bottom: handle.top
281                         anchors.horizontalCenter: handle.horizontalCenter
282                     }
283                 },
284                 State {
285                     name: "Bottom"
286                     AnchorChanges {
287                         target: valueIndicator
288                         anchors.top: handle.bottom
289                         anchors.horizontalCenter: handle.horizontalCenter
290                     }
291                 },
292                 State {
293                     name: "Right"
294                     AnchorChanges {
295                         target: valueIndicator
296                         anchors.left: handle.right
297                         anchors.verticalCenter: handle.verticalCenter
298                     }
299                 },
300                 State {
301                     name: "Left"
302                     AnchorChanges {
303                         target: valueIndicator
304                         anchors.right: handle.left
305                         anchors.verticalCenter: handle.verticalCenter
306                     }
307                 }
308             ]
309         }
310     }
311
312     // when there is no mouse interaction, the handle's position binds to the value
313     Binding {
314         when: !mouseArea.drag.active
315         target: fakeHandle
316         property: "x"
317         value: range.position
318     }
319
320     // when the slider is dragged, the value binds to the handle's position
321     Binding {
322         when: mouseArea.drag.active
323         target: range
324         property: "position"
325         value: fakeHandle.x
326     }
327 }