@@ -15,6 +15,7 @@ struct Knob : ParamWidget { | |||||
/** Multiplier for mouse movement to adjust knob value */ | /** Multiplier for mouse movement to adjust knob value */ | ||||
float speed = 1.0; | float speed = 1.0; | ||||
void onButton(event::Button &e) override; | |||||
void onDragStart(event::DragStart &e) override; | void onDragStart(event::DragStart &e) override; | ||||
void onDragEnd(event::DragEnd &e) override; | void onDragEnd(event::DragEnd &e) override; | ||||
void onDragMove(event::DragMove &e) override; | void onDragMove(event::DragMove &e) override; | ||||
@@ -9,12 +9,14 @@ namespace rack { | |||||
struct ParamWidget : OpaqueWidget { | struct ParamWidget : OpaqueWidget { | ||||
Quantity *quantity = NULL; | Quantity *quantity = NULL; | ||||
float dirtyValue = NAN; | |||||
~ParamWidget() { | ~ParamWidget() { | ||||
if (quantity) | if (quantity) | ||||
delete quantity; | delete quantity; | ||||
} | } | ||||
void step() override; | |||||
/** For legacy patch loading */ | /** For legacy patch loading */ | ||||
void fromJson(json_t *rootJ); | void fromJson(json_t *rootJ); | ||||
void onButton(event::Button &e) override; | void onButton(event::Button &e) override; | ||||
@@ -15,6 +15,7 @@ struct SVGSwitch : virtual ParamWidget, FramebufferWidget { | |||||
SVGWidget *sw; | SVGWidget *sw; | ||||
SVGSwitch(); | SVGSwitch(); | ||||
void step() override; | |||||
/** Adds an SVG file to represent the next switch position */ | /** Adds an SVG file to represent the next switch position */ | ||||
void addFrame(std::shared_ptr<SVG> svg); | void addFrame(std::shared_ptr<SVG> svg); | ||||
void onChange(event::Change &e) override; | void onChange(event::Change &e) override; | ||||
@@ -4,6 +4,15 @@ | |||||
namespace rack { | namespace rack { | ||||
void Knob::onButton(event::Button &e) { | |||||
float r = box.size.x / 2; | |||||
math::Vec c = box.size.div(2); | |||||
float dist = e.pos.minus(c).norm(); | |||||
if (dist <= r) { | |||||
ParamWidget::onButton(e); | |||||
} | |||||
} | |||||
void Knob::onDragStart(event::DragStart &e) { | void Knob::onDragStart(event::DragStart &e) { | ||||
context()->window->cursorLock(); | context()->window->cursorLock(); | ||||
} | } | ||||
@@ -28,9 +37,6 @@ void Knob::onDragMove(event::DragMove &e) { | |||||
if (context()->window->isModPressed()) | if (context()->window->isModPressed()) | ||||
delta /= 16.f; | delta /= 16.f; | ||||
quantity->moveValue(delta); | quantity->moveValue(delta); | ||||
event::Change eChange; | |||||
onChange(eChange); | |||||
} | } | ||||
} | } | ||||
@@ -5,6 +5,20 @@ | |||||
namespace rack { | namespace rack { | ||||
void ParamWidget::step() { | |||||
if (quantity) { | |||||
float value = quantity->getValue(); | |||||
// Trigger change event when quantity value changes | |||||
if (value != dirtyValue) { | |||||
dirtyValue = value; | |||||
event::Change eChange; | |||||
onChange(eChange); | |||||
} | |||||
} | |||||
OpaqueWidget::step(); | |||||
} | |||||
void ParamWidget::fromJson(json_t *rootJ) { | void ParamWidget::fromJson(json_t *rootJ) { | ||||
json_t *valueJ = json_object_get(rootJ, "value"); | json_t *valueJ = json_object_get(rootJ, "value"); | ||||
if (valueJ) { | if (valueJ) { | ||||
@@ -20,7 +34,6 @@ void ParamWidget::onButton(event::Button &e) { | |||||
quantity->reset(); | quantity->reset(); | ||||
// Here's another way of doing it, but either works. | // Here's another way of doing it, but either works. | ||||
// dynamic_cast<ParamQuantity*>(quantity)->getParam()->reset(); | // dynamic_cast<ParamQuantity*>(quantity)->getParam()->reset(); | ||||
return; | |||||
} | } | ||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
@@ -26,8 +26,13 @@ void SVGKnob::setSVG(std::shared_ptr<SVG> svg) { | |||||
} | } | ||||
void SVGKnob::step() { | void SVGKnob::step() { | ||||
// Re-transform TransformWidget if dirty | |||||
if (dirty && quantity) { | |||||
Knob::step(); | |||||
FramebufferWidget::step(); | |||||
} | |||||
void SVGKnob::onChange(event::Change &e) { | |||||
// Re-transform the TransformWidget | |||||
if (quantity) { | |||||
float angle; | float angle; | ||||
if (quantity->isBounded()) { | if (quantity->isBounded()) { | ||||
angle = math::rescale(quantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle); | angle = math::rescale(quantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle); | ||||
@@ -42,12 +47,8 @@ void SVGKnob::step() { | |||||
tw->translate(center); | tw->translate(center); | ||||
tw->rotate(angle); | tw->rotate(angle); | ||||
tw->translate(center.neg()); | tw->translate(center.neg()); | ||||
dirty = true; | |||||
} | } | ||||
FramebufferWidget::step(); | |||||
} | |||||
void SVGKnob::onChange(event::Change &e) { | |||||
dirty = true; | |||||
Knob::onChange(e); | Knob::onChange(e); | ||||
} | } | ||||
@@ -23,18 +23,19 @@ void SVGSlider::setSVGs(std::shared_ptr<SVG> backgroundSVG, std::shared_ptr<SVG> | |||||
} | } | ||||
void SVGSlider::step() { | void SVGSlider::step() { | ||||
if (dirty && quantity) { | |||||
Knob::step(); | |||||
FramebufferWidget::step(); | |||||
} | |||||
void SVGSlider::onChange(event::Change &e) { | |||||
if (quantity) { | |||||
// Interpolate handle position | // Interpolate handle position | ||||
float v = quantity->getScaledValue(); | float v = quantity->getScaledValue(); | ||||
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)); | ||||
dirty = true; | |||||
} | } | ||||
FramebufferWidget::step(); | |||||
} | |||||
void SVGSlider::onChange(event::Change &e) { | |||||
dirty = true; | |||||
ParamWidget::onChange(e); | ParamWidget::onChange(e); | ||||
} | } | ||||
@@ -9,6 +9,11 @@ SVGSwitch::SVGSwitch() { | |||||
addChild(sw); | addChild(sw); | ||||
} | } | ||||
void SVGSwitch::step() { | |||||
ParamWidget::step(); | |||||
FramebufferWidget::step(); | |||||
} | |||||
void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) { | void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) { | ||||
frames.push_back(svg); | frames.push_back(svg); | ||||
// If this is our first frame, automatically set SVG and size | // If this is our first frame, automatically set SVG and size | ||||
@@ -31,7 +31,7 @@ static void drawPlug(NVGcontext *vg, math::Vec pos, NVGcolor color) { | |||||
nvgFill(vg); | nvgFill(vg); | ||||
} | } | ||||
static void drawWire(NVGcontext *vg, math::Vec pos1, math::Vec pos2, NVGcolor color, float width, float tension, float opacity) { | |||||
static void drawWire(NVGcontext *vg, math::Vec pos1, math::Vec pos2, NVGcolor color, float thickness, float tension, float opacity) { | |||||
NVGcolor colorShadow = nvgRGBAf(0, 0, 0, 0.10); | NVGcolor colorShadow = nvgRGBAf(0, 0, 0, 0.10); | ||||
NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5); | NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5); | ||||
@@ -54,7 +54,7 @@ static void drawWire(NVGcontext *vg, math::Vec pos1, math::Vec pos2, NVGcolor co | |||||
nvgMoveTo(vg, pos1.x, pos1.y); | nvgMoveTo(vg, pos1.x, pos1.y); | ||||
nvgQuadTo(vg, pos4.x, pos4.y, pos2.x, pos2.y); | nvgQuadTo(vg, pos4.x, pos4.y, pos2.x, pos2.y); | ||||
nvgStrokeColor(vg, colorShadow); | nvgStrokeColor(vg, colorShadow); | ||||
nvgStrokeWidth(vg, width); | |||||
nvgStrokeWidth(vg, thickness); | |||||
nvgStroke(vg); | nvgStroke(vg); | ||||
// Wire outline | // Wire outline | ||||
@@ -62,12 +62,12 @@ static void drawWire(NVGcontext *vg, math::Vec pos1, math::Vec pos2, NVGcolor co | |||||
nvgMoveTo(vg, pos1.x, pos1.y); | nvgMoveTo(vg, pos1.x, pos1.y); | ||||
nvgQuadTo(vg, pos3.x, pos3.y, pos2.x, pos2.y); | nvgQuadTo(vg, pos3.x, pos3.y, pos2.x, pos2.y); | ||||
nvgStrokeColor(vg, colorOutline); | nvgStrokeColor(vg, colorOutline); | ||||
nvgStrokeWidth(vg, width); | |||||
nvgStrokeWidth(vg, thickness); | |||||
nvgStroke(vg); | nvgStroke(vg); | ||||
// Wire solid | // Wire solid | ||||
nvgStrokeColor(vg, color); | nvgStrokeColor(vg, color); | ||||
nvgStrokeWidth(vg, width - 2); | |||||
nvgStrokeWidth(vg, thickness - 2); | |||||
nvgStroke(vg); | nvgStroke(vg); | ||||
nvgRestore(vg); | nvgRestore(vg); | ||||
@@ -179,17 +179,17 @@ void WireWidget::draw(NVGcontext *vg) { | |||||
opacity = 1.0; | opacity = 1.0; | ||||
} | } | ||||
float width = 5; | |||||
float thickness = 5; | |||||
if (wire && wire->outputModule) { | if (wire && wire->outputModule) { | ||||
Output *output = &wire->outputModule->outputs[wire->outputId]; | Output *output = &wire->outputModule->outputs[wire->outputId]; | ||||
if (output->numChannels != 1) { | if (output->numChannels != 1) { | ||||
width = 8; | |||||
thickness = 8; | |||||
} | } | ||||
} | } | ||||
math::Vec outputPos = getOutputPos(); | math::Vec outputPos = getOutputPos(); | ||||
math::Vec inputPos = getInputPos(); | math::Vec inputPos = getInputPos(); | ||||
drawWire(vg, outputPos, inputPos, color, width, tension, opacity); | |||||
drawWire(vg, outputPos, inputPos, color, thickness, tension, opacity); | |||||
} | } | ||||
void WireWidget::drawPlugs(NVGcontext *vg) { | void WireWidget::drawPlugs(NVGcontext *vg) { | ||||
@@ -21,9 +21,6 @@ void Module::setup(int numParams, int numInputs, int numOutputs, int numLights) | |||||
json_t *Module::toJson() { | json_t *Module::toJson() { | ||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
// id | |||||
json_object_set_new(rootJ, "id", json_integer(id)); | |||||
// params | // params | ||||
json_t *paramsJ = json_array(); | json_t *paramsJ = json_array(); | ||||
for (Param ¶m : params) { | for (Param ¶m : params) { | ||||
@@ -42,11 +39,6 @@ json_t *Module::toJson() { | |||||
} | } | ||||
void Module::fromJson(json_t *rootJ) { | void Module::fromJson(json_t *rootJ) { | ||||
// id | |||||
json_t *idJ = json_object_get(rootJ, "id"); | |||||
if (idJ) | |||||
id = json_integer_value(idJ); | |||||
// params | // params | ||||
json_t *paramsJ = json_object_get(rootJ, "params"); | json_t *paramsJ = json_object_get(rootJ, "params"); | ||||
size_t i; | size_t i; | ||||