4import Qt.labs.animation
7import QGroundControl.Controls
12 property real value: 0
15 property string unitsString
18 required property int decimalPlaces
19 required property real majorTickStepSize
21 property int _tickValueDecimalPlaces: _countDecimalPlaces(majorTickStepSize)
23 property real _indicatorCenterPos: width / 2
25 property real _majorTickSpacing: ScreenTools.defaultFontPixelWidth * 6
27 property real _majorTickSize: valueIndicator.pointerSize + valueIndicator.indicatorValueMargins
28 property real _tickValueEdgeMargin: ScreenTools.defaultFontPixelWidth / 2
29 property real _minorTickSize: _majorTickSize / 2
30 property real _sliderValuePerPixel: majorTickStepSize / _majorTickSpacing
32 property int _minorTickValueStep: majorTickStepSize / 2
34 //property real _sliderValue: _firstPixelValue + ((sliderFlickable.contentX + _indicatorCenterPos) * _sliderValuePerPixel)
36 // Calculate the full range of the slider. We have been given a min/max but that is for clamping the selected slider values.
37 // We need expand that range to take into account additional values that must be displayed above/below the value indicator
38 // when it is at min/max.
40 // Add additional major ticks above/below min/max to ensure we can display the full visual range of the slider
41 property int _majorTicksVisibleBeyondIndicator: Math.ceil(_indicatorCenterPos / _majorTickSpacing)
42 property real _majorTicksExtentsAdjustment: _majorTicksVisibleBeyondIndicator * majorTickStepSize
44 // Calculate the min/max for the full slider range
45 property real _majorTickMinValue: Math.floor((from - _majorTicksExtentsAdjustment) / majorTickStepSize) * majorTickStepSize
46 property real _majorTickMaxValue: Math.floor((to + _majorTicksExtentsAdjustment) / majorTickStepSize) * majorTickStepSize
48 // Now calculate the position we draw the first tick mark such that we are not allowed to flick above the max value
49 property real _firstTickPixelOffset: _indicatorCenterPos - ((from - _majorTickMinValue) / _sliderValuePerPixel)
50 property real _firstPixelValue: _majorTickMinValue - (_firstTickPixelOffset * _sliderValuePerPixel)
52 property int _cMajorTicks: (_majorTickMaxValue - _majorTickMinValue) / majorTickStepSize + 1
54 // Calculate the slider width such that we can flick through the full range of the slider
55 property real _sliderContentSize: ((to - _firstPixelValue) / _sliderValuePerPixel) + (background.width - _indicatorCenterPos)
57 property bool _loadComplete: false
59 property var qgcPal: QGroundControl.globalPalette
61 function setValue(value) {
62 value = _clampedSliderValue(value)
63 if (value !== control.value) {
65 _recalcSliderPos(false)
69 function _outputInternalValues() {
70 console.log("ValueSlider: width", width)
71 console.log("ValueSlider: _indicatorCenterPos", _indicatorCenterPos)
72 console.log("ValueSlider: _firstPixelValue", _firstPixelValue)
73 console.log("ValueSlider: _firstTickPixelOffset", _firstTickPixelOffset)
74 console.log("ValueSlider: _sliderValuePerPixel", _sliderValuePerPixel)
75 console.log("ValueSlider: _indicatorCenterPos", _indicatorCenterPos)
76 console.log("ValueSlider: _majorTickSpacing", _majorTickSpacing)
77 console.log("ValueSlider: _majorTickSize", _majorTickSize)
78 console.log("ValueSlider: _minorTickSize", _minorTickSize)
79 console.log("ValueSlider: _majorTicksVisibleBeyondIndicator", _majorTicksVisibleBeyondIndicator)
80 console.log("ValueSlider: _majorTicksExtentsAdjustment", _majorTicksExtentsAdjustment)
81 console.log("ValueSlider: _majorTickMinValue", _majorTickMinValue)
82 console.log("ValueSlider: _majorTickMaxValue", _majorTickMaxValue)
83 console.log("ValueSlider: _cMajorTicks", _cMajorTicks)
84 console.log("ValueSlider: _sliderContentSize", _sliderContentSize)
87 Component.onCompleted: {
88 _recalcSliderPos(false)
89 //_outputInternalValues()
96 //_outputInternalValues()
100 function _countDecimalPlaces(number) {
101 const numberString = number.toString()
102 if (numberString.includes('.')) {
103 return numberString.split('.')[1].length
109 function _sliderXPosToValue(xPos) {
110 return _firstPixelValue + ((-xPos + _indicatorCenterPos) * _sliderValuePerPixel)
113 function _valueToSliderXPos(value) {
114 return -((value - _firstPixelValue) / _sliderValuePerPixel) + _indicatorCenterPos
117 function _recalcSliderPos(animate = true) {
118 // Position the slider such that the indicator is pointing to the current value
119 let sliderXPos = _valueToSliderXPos(value)
121 flickableAnimation.from = sliderContainer.x
122 flickableAnimation.to = sliderXPos
123 flickableAnimation.start()
125 sliderContainer.x = sliderXPos
129 function _clampedSliderValue(value) {
130 return Math.min(Math.max(value, from), to).toFixed(decimalPlaces)
135 colorGroupEnabled: control.enabled
138 // This TapHandler ensures that the slider captures touch and click events,
139 // preventing them from passing through to the underlying map.
141 acceptedButtons: Qt.AllButtons
142 onTapped: control.forceActiveFocus()
143 grabPermissions: PointerHandler.CanTakeOverFromAnything
147 implicitHeight: _majorTickSize + tickValueMargin + ScreenTools.defaultFontPixelHeight + labelOffset
150 property real tickValueMargin: ScreenTools.defaultFontPixelHeight / 3
151 property real labelOffset: labelItem.visible ? labelItem.contentHeight / 2 : 0
155 y: background.labelOffset
156 width: _sliderContentSize
157 height: background.height - y
160 if (dragHandler.active) {
161 value = _sliderXPosToValue(x)
171 minimum: _valueToSliderXPos(to)
175 PropertyAnimation on x {
176 id: flickableAnimation
178 easing.type: Easing.OutCubic
188 height: sliderContainer.height
189 x: _majorTickSpacing * index + _firstTickPixelOffset
190 opacity: tickValue < from || tickValue > to ? 0.5 : 1
192 property real tickValue: _majorTickMinValue + (majorTickStepSize * index)
197 height: _majorTickSize
202 anchors.bottomMargin: _tickValueEdgeMargin
203 anchors.bottom: parent.bottom
204 anchors.horizontalCenter: majorTickMark.horizontalCenter
205 text: parent.tickValue.toFixed(_tickValueDecimalPlaces)
212 model: _cMajorTicks * 2
215 x: _majorTickSpacing / 2 * index + + _firstTickPixelOffset
217 height: _minorTickSize
219 opacity: tickValue < from || tickValue > to ? 0.5 : 1
220 visible: index % 2 === 1
222 property real tickValue: _majorTickMaxValue - ((majorTickStepSize / 2) * index)
228 id: labelItemBackground
229 width: labelItem.contentWidth
230 height: labelItem.contentHeight
233 visible: labelItem.visible
238 anchors.left: labelItemBackground.left
239 anchors.top: labelItemBackground.top
241 visible: label !== ""
246 implicitHeight: valueIndicator.height
250 anchors.bottom: parent.bottom
251 anchors.horizontalCenter: parent.horizontalCenter
252 width: Math.max(valueLabel.contentWidth + (indicatorValueMargins * 2), pointerSize * 2 + 2)
253 height: valueLabel.contentHeight + (indicatorValueMargins * 2) + pointerSize
255 property real indicatorValueMargins: ScreenTools.defaultFontPixelWidth / 2
256 property real indicatorHeight: valueLabel.contentHeight
257 property real pointerSize: ScreenTools.defaultFontPixelWidth
260 var ctx = getContext("2d")
261 ctx.strokeStyle = qgcPal.text
262 ctx.fillStyle = qgcPal.window
265 ctx.moveTo(width / 2, 0)
266 ctx.lineTo(width / 2 + pointerSize, pointerSize)
267 ctx.lineTo(width - 1, pointerSize)
268 ctx.lineTo(width - 1, height - 1)
269 ctx.lineTo(1, height - 1)
270 ctx.lineTo(1, pointerSize)
271 ctx.lineTo(width / 2 - pointerSize, pointerSize)
279 anchors.bottomMargin: parent.indicatorValueMargins
280 anchors.bottom: parent.bottom
281 anchors.horizontalCenter: parent.horizontalCenter
282 horizontalAlignment: Text.AlignHCenter
283 verticalAlignment: Text.AlignBottom
284 text: _clampedSliderValue(value) + (unitsString !== "" ? " " + unitsString : "")
290 sliderValueTextField.text = _clampedSliderValue(value)
291 sliderValueTextField.visible = true
292 sliderValueTextField.forceActiveFocus()
297 id: sliderValueTextField
298 anchors.topMargin: valueIndicator.pointerSize
301 unitsLabel: unitsString
303 numericValuesOnly: true
308 value = _clampedSliderValue(parseFloat(text))
314 function onValueChanged() { sliderValueTextField.visible = false }