--- a/src/RangedSlider.cpp Thu Mar 31 17:16:44 2022 +0200 +++ b/src/RangedSlider.cpp Thu Mar 31 23:10:57 2022 +0200 @@ -40,21 +40,19 @@ RangedSlider::RangedSlider(QWidget* parent) : QWidget(parent), - _min(0.0), - _max(1.0), - _prefMin(0.25), - _prefMax(0.75), - _val(0.5), - _prec(3), - _tickInterval(0), - _secondaryTicks(1), - _tooltipText(""), - _bgBrush(QColor(255,255,255)), - _prefRangeBrush(QColor(0,0,0)), - _prefRangePen(Qt::NoPen), - _markerBrush(QColor(255,255,255)), - _markerTextIsValue(false), - indicatorTextFont("Arial", + _min(0.0), + _max(1.0), + _prefMin(0.25), + _prefMax(0.75), + _val(0.5), + _prec(3), + _tooltipText(""), + _bgBrush(QColor(255,0,0)), + _prefRangeBrush(QColor(0,127,0)), + _prefRangePen(Qt::NoPen), + _markerBrush(QColor(255,255,255)), + _markerTextIsValue(false), + indicatorTextFont("Arial", 10, QFont::Normal) // Previously we just did the indicator text in 'default' font { @@ -74,96 +72,86 @@ this->repaint(); } -void RangedSlider::setPreferredRange( double min, double max ) +/** + * @brief Set the normal limits in the _prexXxx values and calculate + * the real drawing limits so that we can see if a value is slightly + * out of range. + */ +void RangedSlider::setRange( double min, double max ) { - _prefMin = min; - _prefMax = max; + _prefMin = min; + _prefMax = max; + // Calculate the outer limits + _min = _prefMin - ((_prefMax - _prefMin) * 0.2); + _max = _prefMax + ((_prefMax - _prefMin) * 0.2); - // Only show tooltips if the range has nonzero size. - setMouseTracking(min < max); + // Set the tooltip with the ranges. + _tooltipText = QString("%1 - %2").arg(min, 0, 'f', _prec).arg(max, 0, 'f', _prec); - _tooltipText = QString("%1 - %2").arg(min, 0, 'f', _prec).arg(max, 0, 'f', _prec); - - update(); + update(); } -void RangedSlider::setPreferredRange(QPair<double,double> minmax) -{ - setPreferredRange( minmax.first, minmax.second ); -} - -void RangedSlider::setRange( double min, double max ) -{ - _min = min; - _max = max; - update(); -} void RangedSlider::setRange(QPair<double,double> minmax) { setRange( minmax.first, minmax.second ); } + void RangedSlider::setValue(double value) { - _val = value; -// _valText = QString("%1").arg(_val, 0, 'f', _prec); - update(); + _val = value; + + if (_val < _min) + _val = _min; + if (_val > _max) + _val = _max; - // See comment in constructor for why we call this here - this->setSizes(); - return; + if (_markerTextIsValue) { + _markerText = QString("%1").arg(value, 0, 'f', _prec); + qDebug() << _markerText; + } + + update(); + + // See comment in constructor for why we call this here + //this->setSizes(); + return; } + void RangedSlider::setPrecision(int precision) { - _prec = precision; - update(); + _prec = precision; + update(); } + void RangedSlider::setBackgroundBrush( QBrush const& brush ) { - _bgBrush = brush; - update(); + _bgBrush = brush; + update(); } -void RangedSlider::setPreferredRangeBrush( QBrush const& brush ) -{ - _prefRangeBrush = brush; - update(); -} - -void RangedSlider::setPreferredRangePen( QPen const& pen ) -{ - _prefRangePen = pen; - update(); -} void RangedSlider::setMarkerBrush( QBrush const& brush ) { - _markerBrush = brush; - update(); + _markerBrush = brush; + update(); } void RangedSlider::setMarkerText( QString const& text ) { - _markerText = text; - update(); + _markerText = text; + update(); } void RangedSlider::setMarkerTextIsValue(bool val) { - _markerTextIsValue = val; - update(); + _markerTextIsValue = val; + update(); } -void RangedSlider::setTickMarks( double primaryInterval, int secondaryTicks ) -{ - _secondaryTicks = (secondaryTicks<1)? 1 : secondaryTicks; - _tickInterval = primaryInterval/_secondaryTicks; - - update(); -} void RangedSlider::recalculateHeightInPixels() const { // @@ -209,16 +197,16 @@ // assumptions about space below the baseline are locale-specific, so, say, using ascent() instead of lineSpacing() // could end up painting us into a corner. // - QFontMetrics indicatorTextFontMetrics(this->indicatorTextFont); -// QFontMetrics valueTextFontMetrics(this->valueTextFont); - this->heightInPixels = indicatorTextFontMetrics.lineSpacing()/* + valueTextFontMetrics.lineSpacing()*/; + //QFontMetrics indicatorTextFontMetrics(this->indicatorTextFont); + this->heightInPixels = this->height(); //indicatorTextFontMetrics.lineSpacing(); return; } void RangedSlider::setSizes() { // Caller's responsibility to have recently called this->recalculateHeightInPixels(). (See comment in that function // for how we choose minimum width.) - this->setMinimumSize(2 * this->heightInPixels, this->heightInPixels); +// this->setMinimumSize(2 * this->heightInPixels, this->heightInPixels); + this->setMinimumSize(60, 20); this->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); @@ -237,41 +225,32 @@ void RangedSlider::mouseMoveEvent(QMouseEvent* event) { - event->accept(); + event->accept(); - QPoint tipPoint( mapToGlobal(QPoint(0,0)) ); - QToolTip::showText( tipPoint, _tooltipText, this ); + QPoint tipPoint( mapToGlobal(QPoint(0,0)) ); + QToolTip::showText( tipPoint, _tooltipText, this ); } + void RangedSlider::paintEvent(QPaintEvent* event) { // // Simplistically, the high-level layout of the slider is: // - // |-------------------------------------------------------------| - // | Indicator text | B L A N K | - // |------------------------------------------------+------------| + // |------------------------------------------------| // | <--------------- Graphical Area -------------> | // |------------------------------------------------| // // The graphical area has: // - a background rectangle of the full width of the area, representing the range from this->_min to this->_max // - a foreground rectangle showing the sub-range of this background from this->_prefMin to this->_prefMax - // - a line ("the indicator") showing where this->_val lies in the (this->_min to this->_max) range + // - a vertical line ("the indicator") showing where this->_val lies in the (this->_min to this->_max) range // - // The indicator text sits above the indicator line and shows either its value (this->_valText) or some textual + // The indicator text sits on the center of the area and shows either its value (this->_valText) or some textual // description (eg "Slightly Malty" on the IBU/GU scale) which comes from this->_markerText. // - // In principle, we could have the value text a slightly different height than the graphical area - eg to help - // squeeze into smaller available vertical space on small screens (as we know there is blank space of - // indicatorTextHeight pixels above the space for the value text). - // - // The value text also shows this->_valText. - // - QFontMetrics indicatorTextFontMetrics(this->indicatorTextFont); - int indicatorTextHeight = indicatorTextFontMetrics.lineSpacing(); - int graphicalAreaHeight = this->height() - indicatorTextHeight; + int graphicalAreaHeight = this->height(); // Although the Qt calls take an x- and a y- radius, we want the radius on the rectangle corners to be the same // vertically and horizontally, so only define one measure here. @@ -295,7 +274,7 @@ QPainter painter(this); // Work out the left-to-right (ie x-coordinate) positions of things in the graphical area - double graphicalAreaWidth = this->width()/* - valueTextWidth*/; + double graphicalAreaWidth = this->width(); double range = this->_max - this->_min; double fgRectLeft = graphicalAreaWidth * ((this->_prefMin - this->_min )/range); double fgRectWidth = graphicalAreaWidth * ((this->_prefMax - this->_prefMin)/range); @@ -308,88 +287,53 @@ indicatorLineMiddle = qBound(0.0, indicatorLineMiddle, graphicalAreaWidth - (indicatorLineWidth / 2)); indicatorLineLeft = qBound(0.0, indicatorLineLeft, graphicalAreaWidth - indicatorLineWidth); - // The left-to-right position of the indicator text (also known as marker text) depends on where the slider is. - // First we ask the painter what size rectangle it will need to display this text - painter.setPen(indicatorTextColor); - painter.setFont(this->indicatorTextFont); - QRectF indicatorTextRect = painter.boundingRect(QRectF(), Qt::AlignCenter | Qt::AlignBottom, this->_markerText); - - // Then we use the size of this rectangle to try to make the middle of the text sit over the indicator marker on - // the slider - but bounding things so that the text doesn't go off the edge of the slider. - double indicatorTextLeft = qBound(0.0, - indicatorLineMiddle - (indicatorTextRect.width() / 2), - graphicalAreaWidth - indicatorTextRect.width()); - - // Now we can draw the indicator text - painter.drawText( - indicatorTextLeft, 0, - indicatorTextRect.width(), indicatorTextRect.height(), - Qt::AlignCenter | Qt::AlignBottom, this->_markerText - ); - // All the rest of what we need to do is inside the graphical area, so move the origin to the top-left corner of it - painter.translate(0, indicatorTextRect.height()); + painter.translate(0, 0); painter.setPen(Qt::NoPen); // Make sure anything we draw "inside" the "glass rectangle" stays inside. QPainterPath clipRect; - clipRect.addRoundedRect( QRectF(0, 0, graphicalAreaWidth, graphicalAreaHeight), - rectangleCornerRadius, - rectangleCornerRadius ); + clipRect.addRoundedRect( QRectF(0, 0, graphicalAreaWidth, graphicalAreaHeight), rectangleCornerRadius, rectangleCornerRadius ); painter.setClipPath(clipRect); // Draw the background rectangle. painter.setBrush(_bgBrush); painter.setRenderHint(QPainter::Antialiasing); - painter.drawRoundedRect( QRectF(0, 0, graphicalAreaWidth, graphicalAreaHeight), - rectangleCornerRadius, - rectangleCornerRadius ); + painter.drawRoundedRect( QRectF(0, 0, graphicalAreaWidth, graphicalAreaHeight), rectangleCornerRadius, rectangleCornerRadius ); painter.setRenderHint(QPainter::Antialiasing, false); // Draw the style "foreground" rectangle. painter.save(); - painter.setBrush(_prefRangeBrush); - painter.setPen(_prefRangePen); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawRoundedRect( QRectF(fgRectLeft, 0, fgRectWidth, graphicalAreaHeight), - rectangleCornerRadius, - rectangleCornerRadius ); + painter.setBrush(_prefRangeBrush); + painter.setPen(_prefRangePen); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawRoundedRect( QRectF(fgRectLeft, 0, fgRectWidth, graphicalAreaHeight), rectangleCornerRadius, rectangleCornerRadius ); painter.restore(); // Draw the indicator. painter.setBrush(_markerBrush); painter.drawRect( QRectF(indicatorLineLeft, 0, indicatorLineWidth, graphicalAreaHeight) ); - // Draw a white-to-clear gradient to suggest "glassy." - painter.setBrush(glassBrush); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawRoundedRect( QRectF(0, 0, graphicalAreaWidth, graphicalAreaHeight), - rectangleCornerRadius, - rectangleCornerRadius ); - painter.setRenderHint(QPainter::Antialiasing, false); + // Draw a white-to-clear gradient to suggest "glassy." + painter.setBrush(glassBrush); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawRoundedRect( QRectF(0, 0, graphicalAreaWidth, graphicalAreaHeight), rectangleCornerRadius, rectangleCornerRadius ); + painter.setRenderHint(QPainter::Antialiasing, false); - // Draw the ticks. - painter.setPen(Qt::black); - if( _tickInterval > 0.0 ) - { - int secTick = 1; - for( double currentTick = _min+_tickInterval; _max - currentTick > _tickInterval-1e-6; currentTick += _tickInterval ) - { - painter.translate( graphicalAreaWidth/(_max-_min) * _tickInterval, 0); - if( secTick == _secondaryTicks ) - { - painter.drawLine( QPointF(0,0.25*graphicalAreaHeight), QPointF(0,0.75*graphicalAreaHeight) ); - secTick = 1; - } - else - { - painter.drawLine( QPointF(0,0.333*graphicalAreaHeight), QPointF(0,0.666*graphicalAreaHeight) ); - ++secTick; - } - } - } + // Draw the _markerText in the center of the widget. + // First we ask the painter what size rectangle it will need to display this text + painter.setPen(indicatorTextColor); + painter.setFont(this->indicatorTextFont); + QRectF indicatorTextRect = painter.boundingRect(QRectF(), Qt::AlignCenter | Qt::AlignBottom, this->_markerText); - return; + // Then we use the size of this rectangle to try to calculate the X and Y position for the markerText. + double indicatorTextX = (graphicalAreaWidth - indicatorTextRect.width()) / 2; + double indicatorTextY = (graphicalAreaHeight - indicatorTextRect.height()) / 2; + + // Now we can draw the indicator text + painter.drawText( indicatorTextX, indicatorTextY, indicatorTextRect.width(), indicatorTextRect.height(), Qt::AlignCenter | Qt::AlignBottom, this->_markerText); + + return; }