@@ -24,7 +24,7 @@ struct Knob : ParamWidget { | |||||
/** Fractional value between the param's value and the dragged knob position. */ | /** Fractional value between the param's value and the dragged knob position. */ | ||||
float snapDelta = 0.f; | float snapDelta = 0.f; | ||||
void init() override; | |||||
void initParamQuantity() override; | |||||
void onHover(const event::Hover& e) override; | void onHover(const event::Hover& e) override; | ||||
void onButton(const event::Button& e) override; | void onButton(const event::Button& e) override; | ||||
void onDragStart(const event::DragStart& e) override; | void onDragStart(const event::DragStart& e) override; | ||||
@@ -19,7 +19,10 @@ struct ParamWidget : widget::OpaqueWidget { | |||||
/** For triggering the Change event. `*/ | /** For triggering the Change event. `*/ | ||||
float lastValue = NAN; | float lastValue = NAN; | ||||
virtual void init() {} | |||||
/** Configures ParamQuantity properties based on the type of ParamWidget. | |||||
This seems a bit hacky, but it's easier than requiring plugin developers to set `ParamQuantity::randomizeEnabled`, etc. | |||||
*/ | |||||
virtual void initParamQuantity() {} | |||||
engine::ParamQuantity* getParamQuantity(); | engine::ParamQuantity* getParamQuantity(); | ||||
void step() override; | void step() override; | ||||
void draw(const DrawArgs& args) override; | void draw(const DrawArgs& args) override; | ||||
@@ -19,7 +19,7 @@ struct Switch : ParamWidget { | |||||
bool momentaryPressed = false; | bool momentaryPressed = false; | ||||
bool momentaryReleased = false; | bool momentaryReleased = false; | ||||
void init() override; | |||||
void initParamQuantity() override; | |||||
void step() override; | void step() override; | ||||
void onDoubleClick(const event::DoubleClick& e) override; | void onDoubleClick(const event::DoubleClick& e) override; | ||||
void onDragStart(const event::DragStart& e) override; | void onDragStart(const event::DragStart& e) override; | ||||
@@ -110,7 +110,7 @@ struct Module { | |||||
q->maxValue = maxValue; | q->maxValue = maxValue; | ||||
q->defaultValue = defaultValue; | q->defaultValue = defaultValue; | ||||
if (label == "") | if (label == "") | ||||
q->label = string::f("Parameter %d", paramId + 1); | |||||
q->label = string::f("#%d", paramId + 1); | |||||
else | else | ||||
q->label = label; | q->label = label; | ||||
q->unit = unit; | q->unit = unit; | ||||
@@ -127,7 +127,7 @@ struct Module { | |||||
PortInfo* p = new PortInfo; | PortInfo* p = new PortInfo; | ||||
if (label == "") | if (label == "") | ||||
p->label = string::f("Input %d", portId + 1); | |||||
p->label = string::f("#%d", portId + 1); | |||||
else | else | ||||
p->label = label; | p->label = label; | ||||
inputInfos[portId] = p; | inputInfos[portId] = p; | ||||
@@ -140,7 +140,7 @@ struct Module { | |||||
PortInfo* p = new PortInfo; | PortInfo* p = new PortInfo; | ||||
if (label == "") | if (label == "") | ||||
p->label = string::f("Output %d", portId + 1); | |||||
p->label = string::f("#%d", portId + 1); | |||||
else | else | ||||
p->label = label; | p->label = label; | ||||
outputInfos[portId] = p; | outputInfos[portId] = p; | ||||
@@ -62,7 +62,7 @@ TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { | |||||
o->box.pos = pos; | o->box.pos = pos; | ||||
o->app::ParamWidget::module = module; | o->app::ParamWidget::module = module; | ||||
o->app::ParamWidget::paramId = paramId; | o->app::ParamWidget::paramId = paramId; | ||||
o->init(); | |||||
o->initParamQuantity(); | |||||
return o; | return o; | ||||
} | } | ||||
@@ -12,8 +12,8 @@ namespace app { | |||||
static const float KNOB_SENSITIVITY = 0.0015f; | static const float KNOB_SENSITIVITY = 0.0015f; | ||||
void Knob::init() { | |||||
ParamWidget::init(); | |||||
void Knob::initParamQuantity() { | |||||
ParamWidget::initParamQuantity(); | |||||
engine::ParamQuantity* pq = getParamQuantity(); | engine::ParamQuantity* pq = getParamQuantity(); | ||||
if (pq) { | if (pq) { | ||||
if (snap) | if (snap) | ||||
@@ -20,16 +20,17 @@ struct PortTooltip : ui::Tooltip { | |||||
engine::Port* port = portWidget->getPort(); | engine::Port* port = portWidget->getPort(); | ||||
engine::PortInfo* portInfo = portWidget->getPortInfo(); | engine::PortInfo* portInfo = portWidget->getPortInfo(); | ||||
// Label | // Label | ||||
text = portInfo->label; | |||||
text = (portWidget->type == engine::Port::INPUT) ? "Input" : "Output"; | |||||
text += ": "; | |||||
text += portInfo->label; | |||||
// Voltage, number of channels | // Voltage, number of channels | ||||
int channels = port->getChannels(); | int channels = port->getChannels(); | ||||
for (int i = 0; i < channels; i++) { | for (int i = 0; i < channels; i++) { | ||||
// Add newline or comma | // Add newline or comma | ||||
if (i % 4 == 0) | |||||
text += "\n"; | |||||
else | |||||
text += " "; | |||||
text += string::f("%5gV", port->getVoltage(i)); | |||||
text += "\n"; | |||||
if (channels > 1) | |||||
text += string::f("%d: ", i + 1); | |||||
text += string::f("% .3fV", port->getVoltage(i)); | |||||
} | } | ||||
// Description | // Description | ||||
std::string description = portInfo->description; | std::string description = portInfo->description; | ||||
@@ -64,6 +65,8 @@ PortWidget::PortWidget() { | |||||
} | } | ||||
PortWidget::~PortWidget() { | PortWidget::~PortWidget() { | ||||
// HACK: In case onDragDrop() is called but not onLeave() afterwards... | |||||
destroyTooltip(); | |||||
// plugLight is not a child and is thus owned by the PortWidget, so we need to delete it here | // plugLight is not a child and is thus owned by the PortWidget, so we need to delete it here | ||||
delete plugLight; | delete plugLight; | ||||
// HACK | // HACK | ||||
@@ -236,6 +239,10 @@ void PortWidget::onDragEnd(const event::DragEnd& e) { | |||||
} | } | ||||
void PortWidget::onDragDrop(const event::DragDrop& e) { | void PortWidget::onDragDrop(const event::DragDrop& e) { | ||||
// HACK: Only delete tooltip if we're not (normal) dragging it. | |||||
if (e.origin == this) | |||||
createTooltip(); | |||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | if (e.button != GLFW_MOUSE_BUTTON_LEFT) | ||||
return; | return; | ||||
@@ -258,6 +265,7 @@ void PortWidget::onDragDrop(const event::DragDrop& e) { | |||||
void PortWidget::onDragEnter(const event::DragEnter& e) { | void PortWidget::onDragEnter(const event::DragEnter& e) { | ||||
createTooltip(); | createTooltip(); | ||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | if (e.button != GLFW_MOUSE_BUTTON_LEFT) | ||||
return; | return; | ||||
@@ -278,6 +286,7 @@ void PortWidget::onDragEnter(const event::DragEnter& e) { | |||||
void PortWidget::onDragLeave(const event::DragLeave& e) { | void PortWidget::onDragLeave(const event::DragLeave& e) { | ||||
destroyTooltip(); | destroyTooltip(); | ||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | if (e.button != GLFW_MOUSE_BUTTON_LEFT) | ||||
return; | return; | ||||
@@ -35,10 +35,10 @@ void SvgKnob::onChange(const event::Change& e) { | |||||
// Re-transform the widget::TransformWidget | // Re-transform the widget::TransformWidget | ||||
engine::ParamQuantity* pq = getParamQuantity(); | engine::ParamQuantity* pq = getParamQuantity(); | ||||
if (pq) { | if (pq) { | ||||
float value = pq->getScaledValue(); | |||||
float value = pq->getSmoothValue(); | |||||
float angle; | float angle; | ||||
if (pq->isBounded()) { | if (pq->isBounded()) { | ||||
angle = math::rescale(value, 0.f, 1.f, minAngle, maxAngle); | |||||
angle = math::rescale(value, pq->getMinValue(), pq->getMaxValue(), minAngle, maxAngle); | |||||
} | } | ||||
else { | else { | ||||
// Center unbounded knobs | // Center unbounded knobs | ||||
@@ -34,7 +34,7 @@ void SvgSlider::onChange(const event::Change& e) { | |||||
engine::ParamQuantity* pq = getParamQuantity(); | engine::ParamQuantity* pq = getParamQuantity(); | ||||
if (pq) { | if (pq) { | ||||
// Interpolate handle position | // Interpolate handle position | ||||
float v = pq->getScaledValue(); | |||||
float v = math::rescale(pq->getSmoothValue(), pq->getMinValue(), pq->getMaxValue(), 0.f, 1.f); | |||||
handle->box.pos = math::Vec( | handle->box.pos = math::Vec( | ||||
math::rescale(v, 0.f, 1.f, minHandlePos.x, maxHandlePos.x), | math::rescale(v, 0.f, 1.f, minHandlePos.x, maxHandlePos.x), | ||||
math::rescale(v, 0.f, 1.f, minHandlePos.y, maxHandlePos.y)); | math::rescale(v, 0.f, 1.f, minHandlePos.y, maxHandlePos.y)); | ||||
@@ -9,8 +9,8 @@ namespace rack { | |||||
namespace app { | namespace app { | ||||
void Switch::init() { | |||||
ParamWidget::init(); | |||||
void Switch::initParamQuantity() { | |||||
ParamWidget::initParamQuantity(); | |||||
engine::ParamQuantity* pq = getParamQuantity(); | engine::ParamQuantity* pq = getParamQuantity(); | ||||
if (pq) { | if (pq) { | ||||
pq->snapEnabled = true; | pq->snapEnabled = true; | ||||
@@ -786,9 +786,10 @@ Cable* Engine::getCable(int cableId) { | |||||
return NULL; | return NULL; | ||||
} | } | ||||
void Engine::setParam(Module* module, int paramId, float value) { | void Engine::setParam(Module* module, int paramId, float value) { | ||||
// TODO Does this need to be thread-safe? | |||||
// If being smoothed, cancel smoothing | |||||
// Don't lock because this is called too frequently. | |||||
// If param is being smoothed, cancel smoothing. | |||||
if (internal->smoothModule == module && internal->smoothParamId == paramId) { | if (internal->smoothModule == module && internal->smoothParamId == paramId) { | ||||
internal->smoothModule = NULL; | internal->smoothModule = NULL; | ||||
internal->smoothParamId = 0; | internal->smoothParamId = 0; | ||||