Index: sources/gui/qml/components/RangeSlider.qml =================================================================== diff -u -r2b16e255a35e0f716bfa9d6943b35e4199dcf3b0 -r2ef03b2ce51b4dc507f66e9671953a8e0824bde9 --- sources/gui/qml/components/RangeSlider.qml (.../RangeSlider.qml) (revision 2b16e255a35e0f716bfa9d6943b35e4199dcf3b0) +++ sources/gui/qml/components/RangeSlider.qml (.../RangeSlider.qml) (revision 2ef03b2ce51b4dc507f66e9671953a8e0824bde9) @@ -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 RangeSlider.qml * \author (last) Behrouz NematiPour - * \date (last) 23-Nov-2022 + * \date (last) 31-Mar-2023 * \author (original) Behrouz NematiPour * \date (original) 17-Sep-2020 * @@ -51,6 +51,8 @@ property real maxValueLowerBound : 0 property real maxValueUpperBound : 0 + property bool lowerBoundValid : true + property bool upperBoundValid : true property int curHandler : RangeSlider.HandlerOption.None ///< current active slider handler property int diameter : Variables.progressbarHandler ///< handlers diameter @@ -67,6 +69,16 @@ property bool maxAdjusted : false ///< first time user adjustment happens property bool hasAdjust : false ///< if set to true then component is grayed out until both min and max are adjusted + property bool showTickmarks : true ///< Indicate whether tickMarks should be shown + property bool isTickMarksRound : true ///< Indicate whether the tickmarks are round + property bool isTickMarksCentered : false ///< Indicate whether the tickmarks are centered in the slider body + property int tickMarkYDisplacement : (-diameter + Variables.progressbarHandlerBorderWidth*2) ///< The amount of y displacement if the TickMarks are not centered + + property bool isRoundedEnds : true + property bool hasBorder : true + property color disabledBorderColor : Colors.borderDisableButton + property color enabledBorderColor : Colors.borderButton + onHasAdjustChanged : { setAdjusted() } @@ -79,36 +91,59 @@ /// root attributes clip : false + color: Colors.transparent - height : Variables.progressbarHeight + height : Variables.sliderDefaultBodyHeight touchMargin : 25 leftRightTouchMargin: _root.diameter / 2 + radius : _root.isRoundedEnds ? (height/2) : Variables.rangeRectRadius + + border.width: _root.hasBorder ? Variables.rangeRectBorderWidth : 0 + border.color: (_root.minAdjusted && _root.maxAdjusted) ? enabledBorderColor : disabledBorderColor + minimum : 0 maximum : 0 - /// Lable of the minimum of range + /// The label under the rangeSlider for minimum value minText { visible : true anchors.topMargin: Variables.sliderTextMargin font.pixelSize : Fonts.fontPixelSliderMarker font.bold : false } - ///< Lable of the maximum of range + /// The label under the rangeSlider for maximum value maxText { visible : true anchors.topMargin: Variables.sliderTextMargin font.pixelSize : Fonts.fontPixelSliderMarker font.bold : false } + function incrementMax(vInStepSegments) { + if ( ! maxAdjusted ) { setMaxValue(_root.maxValueUpperBound )} + else { updateMaxValue(vInStepSegments, true )} + } + function decrementMax(vInStepSegments) { + if ( ! maxAdjusted ) { setMaxValue(_root.maxValueUpperBound )} + else { updateMaxValue(vInStepSegments, false)} + } + + function incrementMin(vInStepSegments) { + if ( ! minAdjusted ) { setMinValue(_root.minValueLowerBound )} + else { updateMinValue(vInStepSegments, true )} + } + function decrementMin(vInStepSegments) { + if ( ! minAdjusted ) { setMinValue(_root.minValueLowerBound )} + else { updateMinValue(vInStepSegments, false)} + } /// /// \brief grays out the rangebar and handler if not adjusted and hasAdjust set to true /// function setAdjusted() { function check() { - _rangeRect .color = minAdjusted && maxAdjusted ? Colors.createTreatmentActive : Colors.createTreatmentInactive + _rangeRect .color = minAdjusted && maxAdjusted ? Colors.sliderHighlightColor : Colors.createTreatmentInactive _handlerLeft .color = minAdjusted ? Colors.createTreatmentActive: Colors.createTreatmentInactive _handlerRight.color = maxAdjusted ? Colors.createTreatmentActive: Colors.createTreatmentInactive } @@ -169,11 +204,6 @@ /// \details regarding the current mouse x position selects the correct handler and updated the bound value. /// \param x : mouse x position. function setBound(x) { - if(curHandler === RangeSlider.HandlerOption.None) { - // The user did not select a handle - return - } - let mValue = setValue(x) // console.debug( maxValue - minValue, minValue, value, maxValue ) @@ -194,28 +224,21 @@ else checkLimitsMinValueBounds(mValue) } else { - if ( minAdjusted && maxAdjusted ) { - // min and max have been adjust initially - if ( minDiff < maxDiff ) { - // if (minDiff > limitGap) return // not sure if it needs but kept it as an idea. - checkLimitsMinValueBounds(mValue) - } - else { - // if (maxDiff > limitGap) return // not sure if it needs but kept it as an idea. - checkLimitsMaxValueBounds(mValue) - } - } else { - // if only one handle side is selected, adjust according to the selected - if ( curHandler === RangeSlider.HandlerOption.Max ) checkLimitsMaxValueBounds(mValue) - if ( curHandler === RangeSlider.HandlerOption.Min ) checkLimitsMinValueBounds(mValue) + if ( minDiff < maxDiff ) { + // if (minDiff > limitGap) return // not sure if it needs but kept it as an idea. + checkLimitsMinValueBounds(mValue) } + else { + // if (maxDiff > limitGap) return // not sure if it needs but kept it as an idea. + checkLimitsMaxValueBounds(mValue) + } } } function checkLimitsMinValueBounds ( vValue ) { if ( vValue < minValueLowerBound ) { minValue = minValueLowerBound; return } if ( vValue > minValueUpperBound ) { minValue = minValueUpperBound; return } - setMinvalue(vValue) + setMinValue(vValue) } function checkLimitsMaxValueBounds ( vValue ) { @@ -224,17 +247,89 @@ setMaxValue(vValue) } - function setMinvalue(vValue) { - minAdjusted = true - minValue = vValue + function setMinValue(vValue) { + // added an if block in case for the first time (not adjusted yet) the value is not changed (still on def value), to force the valueChanged emit. + if ( ! minAdjusted ) { + minAdjusted = true + if ( vValue == minValue ) + minValueChanged(vValue) + else + minValue = vValue + } + else { + minValue = vValue + } } function setMaxValue(vValue) { - maxAdjusted = true - maxValue = vValue + // added an if block in case for the first time (not adjusted yet) the value is not changed (still on def value), to force the valueChanged emit. + if ( ! maxAdjusted ) { + maxAdjusted = true + if ( vValue == maxValue ) + maxValueChanged(vValue) + else + maxValue = vValue + } + else { + maxValue = vValue + } } - /// The main range rectangle bar + // This is a helper function that will calculate and return a new value + // that was either incremented or decremented by 1 or step amount + function determineNewValue(vOldValue, vInStepSegments, vIsIncrementing) { + let amountChanged = 1 + if (vInStepSegments) { + amountChanged = step + } + + let newValue = Number.NaN + if(vIsIncrementing) { + newValue = vOldValue + amountChanged + } else { + newValue = vOldValue - amountChanged + } + + // Capping values based on min/max threshold + if ( newValue < minimum ) newValue = minimum + if ( newValue > maximum ) newValue = maximum + + return newValue.toFixed(decimal) + } + + function updateMaxValue(vInStepSegments, vIsIncrementing) { + // Set the "focused" handle to be the maxium handle + curHandler = RangeSlider.HandlerOption.Max + + // In order to use pre-existing rules for handling min/max ranges, need to calculate X of new value + let newMaxValue = determineNewValue(_root.maxValue, vInStepSegments, vIsIncrementing) + let newX = ((newMaxValue - _root.minimum)*_root.width) / (_root.maximum - _root.minimum) + + // Update the boundary + setBound(newX) + } + + function updateMinValue(vInStepSegments, vIsIncrementing) { + // Set the "focused" handle to be the minium handle + curHandler = RangeSlider.HandlerOption.Min + + // In order to use pre-existing rules for handling min/max ranges, need to calculate X of new value + let newMinValue = determineNewValue(_root.minValue, vInStepSegments, vIsIncrementing) + let newX = ((newMinValue - _root.minimum)*_root.width) / (_root.maximum - _root.minimum) + + // Update the boundary + setBound(newX) + } + + function getColor(vIsActive, vIsValid) { + let color = Colors.textMain + if ( ! vIsValid ) { color = Colors.createTreatmentInvalidParam ; return color } + if ( ! vIsActive ) { color = Colors.textDisableButton ; return color } + return color + } + + + /// The main range rectangle bar - This is the "highlighted" area of the slider RangeRect { id: _rangeRect property alias lowerBound : _rangeRect.minimum property alias upperBound : _rangeRect.maximum @@ -244,17 +339,22 @@ x : minmaxDiff ? ((parent.width * (lowerBound - parent.minimum)) / minmaxDiff) : minmaxDiff width : minmaxDiff ? ((parent.width * (upperBound - lowerBound )) / minmaxDiff) : minmaxDiff height : parent.height + radius : _root.isRoundedEnds ? (height/2) : Variables.rangeRectRadius - radius : 0 + border.width: _root.hasBorder ? Variables.rangeRectBorderWidth : 0 + border.color: (_root.minAdjusted && _root.maxAdjusted) ? Colors.sliderProgressBorderActive : Colors.borderDisableButton + decimal : _root.decimal - minText { - visible: false - anchors.topMargin: -40 + minText { // The minimum value label above the handle + visible : false + anchors.topMargin: -60 + color : _root.getColor((_root.minAdjusted && _root.maxAdjusted), lowerBoundValid) } - maxText { - visible: false - anchors.topMargin: -40 + maxText { // the maximum value label above the handle + visible : false + anchors.topMargin: -60 + color : _root.getColor((_root.minAdjusted && _root.maxAdjusted), upperBoundValid) } // propagation is not working on drag ! @@ -277,22 +377,38 @@ /// Tick mark on the slider regarding the defined step within minimum and maximum in the range Loader { id: _ticksLoader active : _root.ticks - anchors.fill : parent - sourceComponent : TickMarks { + width : _root.width + height : (_rangeRect.height - Variables.sliderDefaultRoundTickMarkDiameter) + anchors.bottom : _root.bottom + sourceComponent : _root.showTickmarks ? _tickMarkFactory : undefine + } + + Component { id:_tickMarkFactory + TickMarks { decimal : _root.decimal minimum : _root.minimum maximum : _root.maximum step : _root.step stepSnap : _root.stepSnap textColor : _root.color + lineTickMarkHeight: _rangeRect.height + isTickMarkRound : _root.isTickMarksRound + yDisplacement : { + if ( _root.isTickMarksCentered ) { + return (_root.border.width/2 - 1) // had to subtract 1 to center on the _rangeRect + } else { + return _root.tickMarkYDisplacement + } + } + showEndMarks : true + color: _root.isActive ? Colors.borderButton : Colors.borderDisableButton } } onDragged: { // Need to account for the extended touch areas setBound(vMouseEvent.x - _rangeRect.leftRightTouchMargin) } - onReleased: { // Need to account for the extended touch areas setBound(vMouseEvent.x - _rangeRect.leftRightTouchMargin) @@ -322,7 +438,7 @@ /// Left most handler Rectangle { id: _handlerLeft - property real diameter : _root.diameter + property real diameter : _root.diameter anchors.verticalCenter : parent.verticalCenter anchors.horizontalCenter: _rangeRect.left @@ -332,7 +448,7 @@ radius : diameter color : Colors.highlightProgressBar border { - width : 4 + width : Variables.progressbarHandlerBorderWidth color : Colors.textMain } MouseArea { id: _leftHandleMouseArea @@ -362,7 +478,7 @@ radius : diameter color : Colors.highlightProgressBar border { - width : 4 + width : Variables.progressbarHandlerBorderWidth color : Colors.textMain } MouseArea { id: _rightHandlerMouseArea