1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the Qt Components project.
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
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
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
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."
39 ****************************************************************************/
42 import Qt.labs.components 1.1
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
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
66 property alias inverted: range.inverted
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
73 property int valueIndicatorMargin: 1
74 property string valueIndicatorPosition: __isVertical ? "Left" : "Top"
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;
84 // "Protected" properties
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
93 property bool __isVertical: orientation == Qt.Vertical
96 implicitHeight: handle.height
98 width: __isVertical ? implicitHeight : implicitWidth
99 height: __isVertical ? implicitWidth : implicitHeight
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
104 // - the 'range' does the calculations to map position to/from value,
105 // it also serves as a data storage for both properties;
107 // - the 'fakeHandle' is what the mouse area drags on the screen, it feeds
108 // the 'range' position and also reads it when convenient;
110 // - the real 'handle' it is the visual representation of the handle, that
111 // just follows the 'fakeHandle' position.
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.
117 Component.onCompleted: {
118 __grooveItem.parent = groove;
119 __valueTrackItem.parent = valueTrack;
120 __handleItem.parent = handle;
121 __valueIndicatorItem.parent = valueIndicator;
127 width: __isVertical ? slider.height : slider.width
128 height: __isVertical ? slider.width : slider.height
129 rotation: __isVertical ? -90 : 0
131 anchors.centerIn: slider
140 // XXX: Moved that outside formatValue to get rid of binding loop warnings
142 if (parseInt(v) != v)
143 __useDecimals = true;
145 positionAtMinimum: handle.width / 2
146 positionAtMaximum: contents.width - handle.width / 2
147 onMaximumChanged: __useDecimals = false;
148 onMinimumChanged: __useDecimals = false;
149 onStepSizeChanged: __useDecimals = false;
155 anchors.leftMargin: handle.width / 2
156 anchors.rightMargin: handle.width / 2
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
169 when: slider.inverted
172 anchors.rightMargin: 0
173 anchors.leftMargin: - handle.width / 2
177 anchors.left: handle.horizontalCenter
178 anchors.right: groove.right
185 transform: Translate { x: - handle.width / 2 }
186 rotation: __isVertical ? 90 : 0
188 anchors.verticalCenter: parent.verticalCenter
190 width: __handleItem.width
191 height: __handleItem.height
196 enabled: !mouseArea.drag.active
199 duration: behavior.enabled ? 150 : 0
200 easing.type: Easing.OutSine
208 height: handle.height
209 transform: Translate { x: - handle.width / 2 }
214 property real oldPosition: 0
217 rightMargin: platformStyle.mouseMarginRight
218 leftMargin: platformStyle.mouseMarginLeft
219 topMargin: platformStyle.mouseMarginTop
220 bottomMargin: platformStyle.mouseMarginBottom
223 drag.target: fakeHandle
224 drag.axis: Drag.XAxis
225 drag.minimumX: range.positionAtMinimum
226 drag.maximumX: range.positionAtMaximum
229 oldPosition = range.position;
231 var newX = Math.max(mouse.x + anchors.leftMargin, drag.minimumX);
232 newX = Math.min(newX, drag.maximumX);
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;
241 range.position = oldPosition;
248 transform: Translate {
249 x: - handle.width / 2;
250 y: __isVertical? -(__valueIndicatorItem.width/2)+20 : y ;
253 rotation: __isVertical ? 90 : 0
254 visible: valueIndicatorVisible
256 width: __valueIndicatorItem.width //+ (__isVertical? (handle.width/2) : 0 )
257 height: __valueIndicatorItem.height
261 return slider.valueIndicatorPosition;
263 if (valueIndicatorPosition == "Right")
265 if (valueIndicatorPosition == "Top")
267 if (valueIndicatorPosition == "Bottom")
273 anchors.margins: valueIndicatorMargin
279 target: valueIndicator
280 anchors.bottom: handle.top
281 anchors.horizontalCenter: handle.horizontalCenter
287 target: valueIndicator
288 anchors.top: handle.bottom
289 anchors.horizontalCenter: handle.horizontalCenter
295 target: valueIndicator
296 anchors.left: handle.right
297 anchors.verticalCenter: handle.verticalCenter
303 target: valueIndicator
304 anchors.right: handle.left
305 anchors.verticalCenter: handle.verticalCenter
312 // when there is no mouse interaction, the handle's position binds to the value
314 when: !mouseArea.drag.active
317 value: range.position
320 // when the slider is dragged, the value binds to the handle's position
322 when: mouseArea.drag.active