Index: sources/gui/qml/components/Slider.qml =================================================================== diff -u -r5f6975c6ee7823fc884328418298128cdb97107f -r2f0b2e204b42597e08be24df48a3b3d470b73051 --- sources/gui/qml/components/Slider.qml (.../Slider.qml) (revision 5f6975c6ee7823fc884328418298128cdb97107f) +++ sources/gui/qml/components/Slider.qml (.../Slider.qml) (revision 2f0b2e204b42597e08be24df48a3b3d470b73051) @@ -25,9 +25,11 @@ */ RangeRect { id: _root property alias value : _progressRect.value + property real displayedValue : 0 property real step : 1 property bool stepSnap : false + property bool stepSnapOnRelease : true property bool ticks : false @@ -48,10 +50,46 @@ property int tickMarksThickness : 2 - signal progressRectClicked() - signal progressRectDragged() signal activeChanged() + signal handleSelected() + function incrementValue (vInStepSegments) { + // in case the slider is not yet activated, activate + if (!isActive) { + setActive(true) + } + updateValue(vInStepSegments, true) + } + + function decrementValue (vInStepSegments) { + // in case the slider is not yet activated, activate + if (!isActive) { + setActive(true) + } + updateValue(vInStepSegments, false) + } + + function updateValue(vInStepSegments, vIsIncrement) { + let amountChanged = 1 + if (vInStepSegments) { + amountChanged = step + } + + let newValue = Number.NaN + if(vIsIncrement) { + newValue = _root.value + amountChanged + } else { + newValue = _root.value - amountChanged + } + + // Capping values based on min/max threshold + if ( newValue < minimum ) newValue = minimum + if ( newValue > maximum ) newValue = maximum + + // update new value + update(newValue.toFixed(decimal)) + } + function setActive(active) { if (active) { color = Colors.sliderHighlightColor @@ -60,11 +98,6 @@ color = Colors.createTreatmentInactive handlerColor = Colors.createTreatmentInactive } - - if (active !== isActive) { - isActive = active; - activeChanged() - } } onIsActiveChanged: { @@ -75,9 +108,17 @@ activeChanged() } - height : Variables.progressbarHeight - touchMargin : 25 + onValueChanged: { + if ( _root.isActive ) { + // update the displayed value to the attentive snapped value + _root.displayedValue = calculateRoundedValue(_root.value) + } + } + height : Variables.progressbarHeight + touchMargin : 25 + leftRightTouchMargin: _handler.width/2 + minimum : 0 maximum : 0 @@ -104,6 +145,10 @@ font.bold : false } + function calculateRoundedValue(vValue){ + return (Math.round(vValue / step) * step).toFixed(decimal) + } + function getValueOfX(x) { return ( x * ( maximum - minimum ) ) / width + minimum } @@ -112,42 +157,51 @@ _root.value = vValue } - function setValue(x) { - let mValue = 0 + function calculateValue(x, isSnappingToTicks) { let mMinimum = Number(_root.minimum.toFixed(decimal)) let mMaximum = Number(_root.maximum.toFixed(decimal)) - if ( x < 0 ) { mValue = mMinimum; update(mValue); return; } - if ( x > width ) { mValue = mMaximum; update(mValue); return; } - mValue = getValueOfX(x) + if(x < _handler.width) { + // The outside of the slider, lower bound case + return mMinimum + } - if ( step === 1 ) { mValue = parseInt(mValue); update(mValue); return; } + if(x > _root.width ) { + // The outside of the slider, upper bound case + return mMaximum + } + // Calculate the in-between : + let mValue = 0 let start = 0 + if ( ! stepSnap ) start = mMinimum - mValue = Math.round((mValue - start) / step) * step + start - let decimals = Math.round(-Math.log10(step)) - if (decimals > 0) { - mValue = mValue.toFixed(decimals) + mValue = getValueOfX(x) + + // special handling for the case that the step segments are less than the handle's width + let stepWidth = width / ((mMaximum - mMinimum) / step) + if ( stepWidth < _handler.width ) { + let valueStepCount = parseInt(mValue / step) + mValue = valueStepCount * step + return mValue.toFixed(decimal) } - if ( mValue < mMinimum ) { mValue = mMinimum; update(mValue); return; } - if ( mValue > mMaximum ) { mValue = mMaximum; update(mValue); return; } + if ( isSnappingToTicks ) { + mValue = Math.round((mValue - start) / step) * step + start + let additionalDecimalResolution = 3 + mValue = mValue.toFixed(decimal + additionalDecimalResolution) - update(mValue); return; - } + } - function setHandlerPosition() { - if ( _progressRect.width <= 0 ) { - _handler.x = 0 - _root.tickMarksThickness - } else - if ( _progressRect.width >= _root.width - _root.tickMarksThickness ) { - _handler.x = _root.width - _handler.width + _root.tickMarksThickness + // For sliders with decimal min, max, values, we need to add refinement to + // the value to achieve slider handle movement close to those of the whole number sliders + if ( decimal > 0 ) { } - else { - _handler.x = _progressRect.width - _handler.width / 2 - } + + if ( mValue < mMinimum ) { return mMinimum; } + if ( mValue > mMaximum ) { return mMaximum; } + return mValue; } // used loader for performance since it may not always be required. @@ -169,6 +223,7 @@ } } ProgressRect { id: _progressRect + property real previousSliderValue: Number.NaN color : Colors.transparent decimal : _root.decimal minimum : _root.minimum @@ -189,21 +244,44 @@ } } + function updateHandleValue(vCurrentPositionX) + { + // Passing false for snapping to get exact value in respect to x + let newCurrentValue = calculateValue(vCurrentPositionX, false) + + // Check the cursor's current value to determine if the cursor had moved + // Do not avoid type coercion, it will produce undesired results + if(newCurrentValue != _progressRect.previousSliderValue) { + // The cursor did move + if(stepSnapOnRelease) { + // check for snapping state to save an iteration of recalculation if snap is off + newCurrentValue = calculateValue(vCurrentPositionX, stepSnapOnRelease) + } + + // Update the slider's value since the cursor did move + update(newCurrentValue) + _progressRect.previousSliderValue = newCurrentValue + } + } + onDragged: { - setValue(vMouseEvent.x) + // On position change / dragging, the value is updated based on value calculated + // Need to account for the extended touch areas + let adjustedXPosition = vMouseEvent.x - _progressRect.leftRightTouchMargin + let newCurrentValue = calculateValue(adjustedXPosition, !stepSnapOnRelease) + update(newCurrentValue) } - onClicked: { - setValue(vMouseEvent.x) - } - onPressed: { - setValue(vMouseEvent.x) - } + onReleased: { - setValue(vMouseEvent.x) + if (isActive) { + // Need to account for the extended touch areas + updateHandleValue(vMouseEvent.x - _progressRect.leftRightTouchMargin) + } } Rectangle { id: _handler - property real diameter : Variables.progressbarHandler + property bool handleSelected : false + property real diameter : Variables.progressbarHandler anchors.verticalCenter : parent.verticalCenter anchors.horizontalCenter: _progressRect.right @@ -216,5 +294,18 @@ width: Variables.progressbarHandlerBorderWidth color: Colors.textMain } + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + onPressed: { + // Handle was inactive and was selected, update the value + if ( !_root.isActive ) { + value = inActiveZero ? 0 : minimum + _root.displayedValue = calculateRoundedValue(value) + } + mouse.accepted = false // allow propagtion to the lower mouse areas + handleSelected() // emit + } + } } }