xybrid/xybrid/ui/gadgets/knobgadget.h

135 lines
4.6 KiB
C++

#pragma once
#include "ui/gadgets/gadget.h"
#include "nodelib/param.h"
#include <memory>
#include <functional>
#include <atomic>
#include "util/strings.h"
namespace Xybrid::NodeLib { class ADSR; }
namespace Xybrid::UI {
class LayoutGadget;
class KnobGadget : public Gadget {
Q_OBJECT
bool highlighted = false;
double lastVal = 0.0;
bool dirty = true;
QGraphicsSimpleTextItem* label;
QGraphicsSimpleTextItem* valLabel;
double get();
public:
enum Step : int {
NoStep = 1,
SmallStep = 3,
MedStep = 7,
BigStep = 15,
};
std::function<double()> fGet = [] { return 0.0; };
std::function<void(double)> fSet = [](double) { };
std::function<QString(double)> fText = [](double d) { return QString::number(d); };
double min = 0.0;
double max = 1.0;
double step = 0.01;
double subStep = -1;
int stepPx = SmallStep;
double defaultVal = 0.0;
qreal size = 32;
KnobGadget(QGraphicsItem* parent = nullptr);
~KnobGadget() override = default;
QRectF boundingRect() const override;
QRectF layoutBoundingRect() const override;
QPainterPath shape() const override;
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent*) override;
void mousePressEvent(QGraphicsSceneMouseEvent*) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
void wheelEvent(QGraphicsSceneWheelEvent*) override;
void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override;
inline KnobGadget* setLabel(const QString& s) { label->setText(s); dirty = true; update(); return this; }
inline KnobGadget* setTextFunc(const std::function<QString(double)>& f) { fText = f; dirty = true; update(); return this; }
inline KnobGadget* setRange(double min, double max, double step = -1, int px = -1, double sub = -1) {
this->min = min;
this->max = max;
if (step > 0) this->step = step;
if (px > 0) stepPx = px;
if (sub > 0) subStep = sub;
update();
return this;
}
inline KnobGadget* setDefault(double d) { this->defaultVal = d; return this; }
// and binding templates
template<typename T> KnobGadget* bind(T& ref) {
auto p = &ref; // pointerize
fGet = [p] { return static_cast<double>(*p); };
fSet = [p](double d) { *p = static_cast<T>(d); };
update();
return this;
}
template<typename T> KnobGadget* bind(std::atomic<T>& atm) {
auto p = &atm;
fGet = [p] { return p->load(); };
fSet = [p](double d) { p->store(static_cast<T>(d)); };
update();
return this;
}
KnobGadget* bind(NodeLib::Param& par) {
auto p = &par;
fGet = [p] { return p->value; };
fSet = [p](double d) {
p->value = d;
p->vt = std::numeric_limits<double>::quiet_NaN();
};
// binding to one of these populates metadata
min = p->min;
max = p->max;
defaultVal = p->def;
label->setText(p->name);
dirty = true;
update();
return this;
}
static QString textPercent(double);
static QString textOffset(double);
static QString textFrequency(double);
static QString textGain(double);
static QString textBalance(double);
static void autoCreate(LayoutGadget*, NodeLib::ADSR&);
// macro to vastly reduce boilerplate
#define preset(NAME) \
template<typename T> static inline KnobGadget* auto##NAME (QGraphicsItem* p, T& v) { return auto##NAME (p)->bind(v); } \
static inline KnobGadget* auto##NAME (QGraphicsItem* p)
// common presets
preset(Percent) { return (new KnobGadget(p))->setRange(0.0, 1.0, .01)->setTextFunc(KnobGadget::textPercent); }
preset(Gain) { return (new KnobGadget(p))->setRange(-60, 12, .1)->setLabel(qs("Gain"))->setTextFunc(KnobGadget::textGain); }
preset(Balance) { return (new KnobGadget(p))->setRange(-1.0, 1.0, .01)->setLabel(qs("Balance"))->setTextFunc(KnobGadget::textBalance); }
preset(Cutoff) { return (new KnobGadget(p))->setRange(0, 16000, 10, NoStep, 1)->setLabel(qs("Cutoff"))->setTextFunc(KnobGadget::textFrequency); }
};
}
// keep this local
#undef preset