diff --git a/libs/widgets/circleindicator.cpp b/libs/widgets/circleindicator.cpp new file mode 100644 index 0000000..b09c448 --- /dev/null +++ b/libs/widgets/circleindicator.cpp @@ -0,0 +1,174 @@ +#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(); +} diff --git a/libs/widgets/circleindicator.h b/libs/widgets/circleindicator.h new file mode 100644 index 0000000..26af7ce --- /dev/null +++ b/libs/widgets/circleindicator.h @@ -0,0 +1,80 @@ +#ifndef circleindicator_H +#define circleindicator_H + +#include "qad_widgets_export.h" + +#include +#include +#include +#include + + +class QAD_WIDGETS_EXPORT CircleIndicator: public QWidget { + Q_OBJECT + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor) + Q_PROPERTY(QColor backColor READ backColor WRITE setBackColor) + Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor) + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(double angleStartOffset READ angleStartOffset WRITE setAngleStartOffset) + Q_PROPERTY(double angleGap READ angleGap WRITE setAngleGap) + Q_PROPERTY(double barSize READ barSize WRITE setBarSize) + Q_PROPERTY(double value READ value WRITE setValue) + Q_PROPERTY(double minimum READ minimum WRITE setMinimum) + Q_PROPERTY(double maximum READ maximum WRITE setMaximum) + +public: + explicit CircleIndicator(QWidget * parent = 0); + ~CircleIndicator(); + + double value() const { return m_value; } + double minimum() const { return m_minimum; } + double maximum() const { return m_maximum; } + + QColor backColor() const { return back_color; } + void setBackColor(const QColor & c); + + QColor fillColor() const { return fill_color; } + void setFillColor(const QColor & c); + + QColor borderColor() const { return border_color; } + void setBorderColor(const QColor & c); + + QString text() const { return m_text; } + void setText(const QString & t); + + double angleStartOffset() const { return m_angle_offset; } + void setAngleStartOffset(double a); + + double angleGap() const { return angle_gap; } + void setAngleGap(double a); + + double barSize() const { return bar_; } + void setBarSize(double a); + + int heightForWidth(int) const; + bool hasHeightForWidth() const; + +private: + void init(); + QSize minimumSizeHint() const; + QSize sizeHint() const; + void paintEvent(QPaintEvent *); + void animate(); + + QColor fill_color, back_color, border_color; + QString m_text; + QVariantAnimation anim; + double m_value = 0., m_value_visual = 0., m_minimum = 0., m_maximum = 100.; + double m_angle_offset = 0., angle_gap = 20., bar_ = 30.; + +public slots: + void enable() { setEnabled(true); } + void disable() { setEnabled(false); } + + void setValue(double v); + void setMinimum(double m); + void setMaximum(double m); +}; + + +#endif diff --git a/libs/widgets/plugin/circleindicatorplugin.cpp b/libs/widgets/plugin/circleindicatorplugin.cpp new file mode 100644 index 0000000..6dbe076 --- /dev/null +++ b/libs/widgets/plugin/circleindicatorplugin.cpp @@ -0,0 +1,69 @@ +#include "circleindicatorplugin.h" + +#include "circleindicator.h" + +#include + + +CircleIndicatorPlugin::CircleIndicatorPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void CircleIndicatorPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool CircleIndicatorPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * CircleIndicatorPlugin::createWidget(QWidget * parent) { + return new CircleIndicator(parent); +} + + +QString CircleIndicatorPlugin::name() const { + return QLatin1String("CircleIndicator"); +} + + +QString CircleIndicatorPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon CircleIndicatorPlugin::icon() const { + return QIcon(/*":/icons/clineedit.png"*/); +} + + +QString CircleIndicatorPlugin::toolTip() const { + return QLatin1String("Circle indicator with text"); +} + + +QString CircleIndicatorPlugin::whatsThis() const { + return QLatin1String("Circle indicator with text"); +} + + +bool CircleIndicatorPlugin::isContainer() const { + return false; +} + + +QString CircleIndicatorPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString CircleIndicatorPlugin::includeFile() const { + return QLatin1String("circleindicator.h"); +} diff --git a/libs/widgets/plugin/circleindicatorplugin.h b/libs/widgets/plugin/circleindicatorplugin.h new file mode 100644 index 0000000..ca24ef9 --- /dev/null +++ b/libs/widgets/plugin/circleindicatorplugin.h @@ -0,0 +1,36 @@ +#ifndef circleindicatorplugin_H +#define circleindicatorplugin_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class CircleIndicatorPlugin + : public QObject + , public QDesignerCustomWidgetInterface { + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + CircleIndicatorPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; +}; + +#endif diff --git a/libs/widgets/plugin/qad_widgets.cpp b/libs/widgets/plugin/qad_widgets.cpp index a93d2f5..bddb279 100644 --- a/libs/widgets/plugin/qad_widgets.cpp +++ b/libs/widgets/plugin/qad_widgets.cpp @@ -1,5 +1,6 @@ #include "qad_widgets.h" +#include "circleindicatorplugin.h" #include "clineeditplugin.h" #include "colorbuttonplugin.h" #include "ecomboboxplugin.h" @@ -21,24 +22,28 @@ QADWidgets::QADWidgets(QObject * parent): QObject(parent) { - m_widgets.append(new SpinSliderPlugin(this)); - m_widgets.append(new ColorButtonPlugin(this)); - m_widgets.append(new ShortcutsPlugin(this)); - m_widgets.append(new CLineEditPlugin(this)); - m_widgets.append(new QIPEditPlugin(this)); - m_widgets.append(new QPointEditPlugin(this)); - m_widgets.append(new QRectEditPlugin(this)); - m_widgets.append(new EComboBoxPlugin(this)); - m_widgets.append(new QPIConsolePlugin(this)); - m_widgets.append(new IconedLabelPlugin(this)); - m_widgets.append(new QCodeEditPlugin(this)); - m_widgets.append(new QVariantEditPlugin(this)); - m_widgets.append(new QPIConfigPlugin(this)); - m_widgets.append(new EvalSpinBoxPlugin(this)); - m_widgets.append(new ImageViewPlugin(this)); - m_widgets.append(new ScrollSpinBoxPlugin(this)); - m_widgets.append(new RangeSliderPlugin(this)); - m_widgets.append(new StateIconPlugin(this)); + // clang-format off + m_widgets + << new SpinSliderPlugin(this) + << new ColorButtonPlugin(this) + << new ShortcutsPlugin(this) + << new CLineEditPlugin(this) + << new QIPEditPlugin(this) + << new QPointEditPlugin(this) + << new QRectEditPlugin(this) + << new EComboBoxPlugin(this) + << new QPIConsolePlugin(this) + << new IconedLabelPlugin(this) + << new QCodeEditPlugin(this) + << new QVariantEditPlugin(this) + << new QPIConfigPlugin(this) + << new EvalSpinBoxPlugin(this) + << new ImageViewPlugin(this) + << new ScrollSpinBoxPlugin(this) + << new RangeSliderPlugin(this) + << new StateIconPlugin(this) + << new CircleIndicatorPlugin(this); + // clang-format on }