Mon, 06 Jun 2022 19:35:39 +0200
Updated the changes from the product misc editor to the recipe misc editor.
/* * RangedSlider.cpp is part bmsapp. * * Original written for Brewtarget, and is Copyright the following * authors 2009-2020 * - Matt Young <mfsy@yahoo.com> * - Mik Firestone <mikfire@gmail.com> * - Philip G. Lee <rocketman768@gmail.com> * * bmsapp is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bmsapp is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "RangedSlider.h" #include <QPaintEvent> #include <QPainter> #include <QColor> #include <QPalette> #include <QApplication> #include <QRectF> #include <QFont> #include <QFontMetrics> #include <QMouseEvent> #include <QLabel> #include <QToolTip> #include <QLinearGradient> #include <QPainterPath> #include <QDebug> RangedSlider::RangedSlider(QWidget* parent) : QWidget(parent), _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 { // Fixed minimum size. this->setMinimumSize(60, 20); this->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); // There no particular reason to limit our horizontal size, so, in principle, this call asks that there be no such // (practical) limit. this->setMaximumWidth(QWIDGETSIZE_MAX); // Generate mouse move events whenever mouse movers over widget. this->setMouseTracking(true); this->repaint(); } /** * @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; // Calculate the outer limits _min = _prefMin - ((_prefMax - _prefMin) * 0.2); _max = _prefMax + ((_prefMax - _prefMin) * 0.2); // Set the tooltip with the ranges. _tooltipText = QString("%1 - %2").arg(min, 0, 'f', _prec).arg(max, 0, 'f', _prec); update(); } void RangedSlider::setRange(QPair<double,double> minmax) { setRange( minmax.first, minmax.second ); } void RangedSlider::setValue(double value) { _val = value; if (_val < _min) _val = _min; if (_val > _max) _val = _max; if (_markerTextIsValue) { _markerText = QString("%1").arg(value, 0, 'f', _prec); } update(); return; } void RangedSlider::setPrecision(int precision) { _prec = precision; update(); } void RangedSlider::setBackgroundBrush( QBrush const& brush ) { _bgBrush = brush; update(); } void RangedSlider::setMarkerBrush( QBrush const& brush ) { _markerBrush = brush; update(); } void RangedSlider::setMarkerText( QString const& text ) { _markerText = text; update(); } void RangedSlider::setMarkerTextIsValue(bool val) { _markerTextIsValue = val; update(); } void RangedSlider::mouseMoveEvent(QMouseEvent* event) { event->accept(); 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: // // |------------------------------------------------| // | <--------------- 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 vertical line ("the indicator") showing where this->_val lies in the (this->_min to this->_max) range // // 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. // 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. int rectangleCornerRadius = graphicalAreaHeight / 4; static const QPalette palette(QApplication::palette()); static const int indicatorLineWidth = 4; static const QColor fgRectColor(0,127,0); static const QColor indicatorTextColor(0,0,0); static const QColor valueTextColor(0,127,0); QLinearGradient glassGrad( QPointF(0,0), QPointF(0,graphicalAreaHeight) ); glassGrad.setColorAt( 0, QColor(255,255,255,127) ); glassGrad.setColorAt( 1, QColor(255,255,255,0) ); QBrush glassBrush(glassGrad); // Per https://doc.qt.io/qt-5/highdpi.html, for best High DPI display support, we need to: // • Always use the qreal versions of the QPainter drawing API // • Size windows and dialogs in relation to the corresponding screen size // • Replace hard-coded sizes in layouts and drawing code with values calculated from font metrics or screen size QPainter painter(this); // Work out the left-to-right (ie x-coordinate) positions of things in the graphical area 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); double indicatorLineMiddle = graphicalAreaWidth * ((this->_val - this->_min )/range); double indicatorLineLeft = indicatorLineMiddle - (indicatorLineWidth / 2); // Make sure all coordinates are valid. fgRectLeft = qBound(0.0, fgRectLeft, graphicalAreaWidth); fgRectWidth = qBound(0.0, fgRectWidth, graphicalAreaWidth - fgRectLeft); indicatorLineMiddle = qBound(0.0, indicatorLineMiddle, graphicalAreaWidth - (indicatorLineWidth / 2)); indicatorLineLeft = qBound(0.0, indicatorLineLeft, graphicalAreaWidth - indicatorLineWidth); // 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, 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 ); painter.setClipPath(clipRect); // Draw the background rectangle. painter.setBrush(_bgBrush); painter.setRenderHint(QPainter::Antialiasing); 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.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 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); // 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; } void RangedSlider::moveEvent(QMoveEvent *event) { QWidget::moveEvent(event); return; }