#include "circleindicator.h" #include "qad_types.h" #include #include #include #include #include #include #include template inline T qClamp(T v, T min, T max) { return qMin(max, qMax(min, v)); } CircleIndicator::CircleIndicator(QWidget * parent): QWidget(parent) { init(); } CircleIndicator::~CircleIndicator() {} int CircleIndicator::heightForWidth(int) const { return width(); } bool CircleIndicator::hasHeightForWidth() const { return false; } void CircleIndicator::init() { connect(&anim, &QVariantAnimation::valueChanged, this, [this](QVariant v) { m_value_visual = v.toDouble(); update(); }); anim.setDuration(200); anim.setEasingCurve(QEasingCurve::OutQuad); fill_color = QColor(255, 245, 60); back_color = QColor(216, 216, 216); border_color = Qt::darkGray; } QSize CircleIndicator::minimumSizeHint() const { return QSize(40, 40) * appScale(); } QSize CircleIndicator::sizeHint() const { return QSize(120, 120) * appScale(); } void CircleIndicator::paintEvent(QPaintEvent *) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::SmoothPixmapTransform); float w = width(), h = height(), r = qMin(w, h) / 2.f, thick = qMax(1., r / 80.); r -= thick; float thin = qMax(1., thick / 3.), lr = r * bar_ / 100.; p.translate(w / 2.f, h / 2.f); p.save(); p.rotate(m_angle_offset + 90.); QRectF rct(-r, -r, r + r, r + r); double range = maximum() - minimum(); double nv = qClamp((m_value_visual - minimum()) / qMax(range, 1.E-3), 0., 1.) * 100.; QPainterPath path, path2; QPointF tp; path.arcMoveTo(rct, angle_gap); tp = path.currentPosition(); path.arcTo(rct, angle_gap, 360 - angle_gap - angle_gap); rct.adjust(lr, lr, -lr, -lr); path2.arcMoveTo(rct, -angle_gap); path2.arcTo(rct, -angle_gap, -360 + angle_gap + angle_gap); path2.lineTo(tp); path.connectPath(path2); p.setPen(QPen(border_color, thick)); QConicalGradient grad; double v = qClamp((1. - nv / (100. - thin / 3.6)) * ((360. - angle_gap - angle_gap) / 360.) + (angle_gap / 360.), 0., 1.); grad.setColorAt(1., fill_color); grad.setColorAt(qClamp(v + thin / 360., 0., 1.), fill_color); grad.setColorAt(v, back_color); grad.setColorAt(0., back_color); p.setBrush(grad); p.drawPath(path); float hs = (r - lr) / 1.2; QRect ir(-hs, -hs, hs + hs, hs + hs); p.restore(); if (m_text.isEmpty()) return; p.setPen(palette().text().color()); auto tbr = p.boundingRect({}, m_text); if (tbr.isEmpty()) return; double tscl = qMin(ir.width() / tbr.width(), ir.height() / tbr.height()); p.scale(tscl, tscl); p.drawText(ir, Qt::AlignCenter, m_text); } void CircleIndicator::animate() { anim.stop(); anim.setStartValue(m_value_visual); anim.setEndValue(m_value); anim.start(); } void CircleIndicator::setValue(double v) { if (m_value == v) return; m_value = qClamp(v, minimum(), maximum()); animate(); } void CircleIndicator::setMinimum(double ll) { m_minimum = ll; setValue(qClamp(value(), minimum(), maximum())); update(); } void CircleIndicator::setMaximum(double hl) { m_maximum = hl; setValue(qClamp(value(), minimum(), maximum())); update(); } void CircleIndicator::setFillColor(const QColor & c) { fill_color = c; update(); } void CircleIndicator::setBorderColor(const QColor & c) { border_color = c; update(); } void CircleIndicator::setText(const QString & t) { m_text = t; update(); } void CircleIndicator::setBackColor(const QColor & c) { back_color = c; update(); } void CircleIndicator::setAngleGap(double a) { angle_gap = qClamp(a, 0., 180.); update(); } void CircleIndicator::setBarSize(double a) { bar_ = qClamp(a, 0., 100.); update(); } void CircleIndicator::setAngleStartOffset(double a) { m_angle_offset = a; update(); }