Index: sources/gui/qml/components/Slider.qml =================================================================== diff -u -r6d612c93d9a38fde3fadc93766976b401cf76652 -r83b9d737cd495b34a7b42f5409962a9442f3b8f4 --- sources/gui/qml/components/Slider.qml (.../Slider.qml) (revision 6d612c93d9a38fde3fadc93766976b401cf76652) +++ sources/gui/qml/components/Slider.qml (.../Slider.qml) (revision 83b9d737cd495b34a7b42f5409962a9442f3b8f4) @@ -1,13 +1,13 @@ /*! * - * Copyright (c) 2020-2023 Diality Inc. - All Rights Reserved. + * Copyright (c) 2020-2024 Diality Inc. - All Rights Reserved. * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file Slider.qml * \author (last) Behrouz NematiPour - * \date (last) 10-Feb-2023 + * \date (last) 26-Jan-2024 * \author (original) Behrouz NematiPour * \date (original) 18-Mar-2020 * @@ -24,15 +24,16 @@ * \brief Denali project ProgressBar */ RangeRect { id: _root - property alias value : _progressRect.value - property real displayedValue : 0 + property real value : _progressRect.value + property real defaultValue : _root.minimum property real step : 1 property bool stepSnap : false property bool stepSnapOnRelease : true property bool ticks : false + // TODO suggest to BN that we should rename color to progressBarBgColor because slider's rangeRect has a color property also property alias color : _progressRect.color property alias bgColor : _root.color @@ -49,43 +50,108 @@ property int tickMarksThickness : 2 + property bool isRoundedEnds : true + property bool hasBorder : true + property color borderColor : Colors.borderDisableButton + signal activeChanged() signal handleSelected() + signal sliderSelected() - function setActive(active) { + onDefaultValueChanged: refreshValue() + + // this function shall be used in case that any external value is forced to be set for the slider + // like the OFF switch wants to externally set the slider value and bypass slider controlls and checks. + // same is used in the main treatment Blood,dialyzer sliders to be set to the current value when get in to adjustment screen. + function reset(vValue) { + _root.value = vValue + _progressRect.value = vValue + } + + function incrementValue (vInStepSegments) { + updateValue(vInStepSegments, true) + } + + function decrementValue (vInStepSegments) { + updateValue(vInStepSegments, false) + } + + function updateValue(vInStepSegments, vIsIncrement) { + let amountChanged = 1 + if (vInStepSegments) { + amountChanged = step + } + + let newValue = Number.NaN + if(vIsIncrement) { + newValue = _progressRect.value + amountChanged + } else { + newValue = _progressRect.value - amountChanged + } + + // Capping values based on min/max threshold + if ( newValue < minimum ) newValue = minimum + if ( newValue > maximum ) newValue = maximum + + // Update the slider's visual value + _progressRect.previousSliderValue = newValue // for comparison purposes + _progressRect.value = newValue // visual value + + // update slider value with rounded new value + update(newValue) + } + + function setActiveVisuals(active) { if (active) { - color = Colors.createTreatmentActive + color = Colors.sliderHighlightColor handlerColor = Colors.createTreatmentActive } else { - color = Colors.createTreatmentInactive + color = Colors.createTreatmentInactive handlerColor = Colors.createTreatmentInactive } } - onIsActiveChanged: { - setActive(isActive) - if (!isActive) { - value = inActiveZero ? 0 : minimum + /* + refreshValue() re-evaluates the _root.value and _progressRect.value based on the + current active state. This function is used when the defaultValue property or + isActive property changes. + */ + function refreshValue() { + // value is assigned a different value based on active-ness of the slider + // This is to resolve the use of slider with a switch and arrows enabled. + // It allows correct behavior when using arrow on a first initialize increment of + // the slider when the slider has inActiveZero to true. + if(!isActive) { + _root.value = inActiveZero ? 0 : _root.defaultValue + } else { + _root.value = _root.defaultValue } - activeChanged() + + // need to set the value of progressRect to reflect handle position + _progressRect.value = _root.defaultValue } - onValueChanged: { - if ( _root.isActive ) { - // update the displayed value to the attentive snapped value - _root.displayedValue = calculateRoundedValue(_root.value) - } + onIsActiveChanged: { + setActiveVisuals(isActive) + refreshValue () // re-evaluate values + activeChanged() // emit } - height : Variables.progressbarHeight + height : Variables.sliderDefaultBodyHeight touchMargin : 25 leftRightTouchMargin: _handler.width/2 + radius : _root.isRoundedEnds ? (height/2) : Variables.rangeRectRadius + + border.width: _root.hasBorder ? Variables.rangeRectBorderWidth : 0 + border.color: _root.isActive ? Colors.borderButton : Colors.borderDisableButton + minimum : 0 maximum : 0 // real-time bound change should effect the current set value onMinimumChanged: { + if ( !isActive && inActiveZero ) { value = 0; return } if (value < minimum ) value = minimum } @@ -123,7 +189,9 @@ let mMinimum = Number(_root.minimum.toFixed(decimal)) let mMaximum = Number(_root.maximum.toFixed(decimal)) - if(x < _handler.width) { + // the center of the handler is aligned on the snap point and half width shall be used to set as min not the entire width. + // also half of the hadler is out of slider min position when set on min, which proves the same as above. + if(x < ( _handler.width / 2 ) ) { // The outside of the slider, lower bound case return mMinimum } @@ -139,6 +207,7 @@ if ( ! stepSnap ) start = mMinimum + // calculate the expected value based on x mValue = getValueOfX(x) // special handling for the case that the step segments are less than the handle's width @@ -149,33 +218,58 @@ return mValue.toFixed(decimal) } - if ( isSnappingToTicks ) { - mValue = Math.round((mValue - start) / step) * step + start + // 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 ) { let additionalDecimalResolution = 3 mValue = mValue.toFixed(decimal + additionalDecimalResolution) - } - // 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 ) { + if ( isSnappingToTicks ) { + mValue = Math.round((mValue - start) / step) * step + start + mValue = mValue.toFixed(decimal) } if ( mValue < mMinimum ) { return mMinimum; } if ( mValue > mMaximum ) { return mMaximum; } return mValue; } + // used loader for performance since it may not always be required. + // and can be a heavy Component + Loader { id: _ticksLoader + active : ticks + anchors.fill : parent + sourceComponent : TickMarks { + decimal : _root.decimal + minimum : _root.minimum + maximum : _root.maximum + step : _root.step + stepSnap : _root.stepSnap + textColor : _root.color + lineTickMarkHeight : _progressRect.height + lineTickMarkThickness : _root.tickMarksThickness + isTickMarkRound : _root.isRoundedEnds + yDisplacement : _root.isRoundedEnds ? (-_handler.height/2 - _handler.border.width/2) : 0 + color : _root.isActive ? Colors.borderButton : Colors.borderDisableButton + } + } + ProgressRect { id: _progressRect property real previousSliderValue: Number.NaN - color : Colors.highlightProgressBar + value : minimum + color : Colors.sliderHighlightColor decimal : _root.decimal + minimum : _root.minimum maximum : _root.maximum - height: 5 - leftRightTouchMargin: _handler.width/2 + radius : _root.isRoundedEnds ? (height/2) : Variables.rangeRectRadius + + border.width: _root.hasBorder ? Variables.rangeRectBorderWidth : 0 + border.color: _root.isActive ? Colors.sliderProgressBorderActive : Colors.borderDisableButton + // propagation is not working on drag ! onDragged: { _root.dragged(vMouseEvent) @@ -188,22 +282,6 @@ } } - // used loader for performance since it may not always be required. - // and can be a heavy Component - Loader { id: _ticksLoader - active : ticks - anchors.fill : parent - sourceComponent : TickMarks { - thickness : _root.tickMarksThickness - decimal : _root.decimal - minimum : _root.minimum - maximum : _root.maximum - step : _root.step - stepSnap : _root.stepSnap - textColor : _root.color - } - } - function updateHandleValue(vCurrentPositionX) { // Passing false for snapping to get exact value in respect to x @@ -212,15 +290,20 @@ // 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 + + // 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 + _progressRect.previousSliderValue = newCurrentValue // for comparison purposes + _progressRect.value = newCurrentValue // visual value + + // update slider value with rounded new value + update(calculateRoundedValue(newCurrentValue)) + } } @@ -229,14 +312,20 @@ // Need to account for the extended touch areas let adjustedXPosition = vMouseEvent.x - _progressRect.leftRightTouchMargin let newCurrentValue = calculateValue(adjustedXPosition, !stepSnapOnRelease) - update(newCurrentValue) + _progressRect.value = newCurrentValue // update visual active slider bar + + update(calculateRoundedValue(newCurrentValue)) // displayed/slider value } + onPressed: { + // Indicate that the slider body was selected to provide handling/activation + sliderSelected() // emit + + updateHandleValue(vMouseEvent.x - _progressRect.leftRightTouchMargin) + } onReleased: { - if (isActive) { - // Need to account for the extended touch areas - updateHandleValue(vMouseEvent.x - _progressRect.leftRightTouchMargin) - } + // Need to account for the extended touch areas + updateHandleValue(vMouseEvent.x - _progressRect.leftRightTouchMargin) } Rectangle { id: _handler @@ -258,11 +347,6 @@ 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 }