From 0798f2844dfff1b094f3dedd7d27c51eef0113e1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 13 Mar 2018 00:15:28 -0400 Subject: [PATCH] Reorganize ParamWidgets, introduce Component as shared base class --- README.md | 4 +- include/app.hpp | 93 +++++---- include/componentlibrary.hpp | 37 +++- res/ComponentLibrary/PB61303.svg | 217 +-------------------- src/Core/QuadMIDIToCVInterface.cpp | 59 ++++-- src/app/LightWidget.cpp | 2 +- src/app/MomentarySwitch.cpp | 18 ++ src/app/{ParamWidget.cpp => Parameter.cpp} | 12 +- src/app/SVGButton.cpp | 32 +++ src/app/SVGSwitch.cpp | 2 +- src/app/ToggleSwitch.cpp | 17 ++ src/main.cpp | 2 +- 12 files changed, 222 insertions(+), 273 deletions(-) create mode 100644 src/app/MomentarySwitch.cpp rename src/app/{ParamWidget.cpp => Parameter.cpp} (75%) create mode 100644 src/app/SVGButton.cpp create mode 100644 src/app/ToggleSwitch.cpp diff --git a/README.md b/README.md index 9b03d1c2..5d5c5375 100644 --- a/README.md +++ b/README.md @@ -89,8 +89,8 @@ All **source code** in this repository is licensed under [BSD-3-Clause](LICENSE. **Component Library graphics** in `res/ComponentLibrary` are licensed under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/) by [Grayscale](http://grayscale.info/). Commercial plugins must request a commercial license to use Component Library graphics by emailing contact@vcvrack.com. -**Core** panel graphics in `res/Core` are copyright © 2017 by Grayscale. You may not create derivative works of Core panels. +**Core** panel graphics in `res/Core` are copyright © 2017 Grayscale. You may not create derivative works of Core panels. The **VCV logo and icon** are copyright © 2017 Andrew Belt and may not be used in derivative works. -The **"VCV" brand name** is trademarked and may not be used for unofficial products. However, it is acceptable to use the phrase "for VCV Rack" for promotion of your plugin. For all other purposes, email contact@vcvrack.com. +The **"VCV" name** is trademarked and may not be used for unofficial products. However, it is acceptable to use the phrase "for VCV Rack" for promotion of your plugin. For all other purposes, email contact@vcvrack.com. diff --git a/include/app.hpp b/include/app.hpp index 64db4f36..60e527b9 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -36,7 +36,7 @@ struct Module; struct Wire; struct RackWidget; -struct ParamWidget; +struct Parameter; struct Port; struct SVGPanel; @@ -58,14 +58,14 @@ struct ModuleWidget : OpaqueWidget { SVGPanel *panel = NULL; std::vector inputs; std::vector outputs; - std::vector params; + std::vector params; ModuleWidget(Module *module); ~ModuleWidget(); /** Convenience functions for adding special widgets (calls addChild()) */ void addInput(Port *input); void addOutput(Port *output); - void addParam(ParamWidget *param); + void addParam(Parameter *param); void setPanel(std::shared_ptr svg); virtual json_t *toJson(); @@ -200,16 +200,29 @@ struct SVGPanel : FramebufferWidget { }; //////////////////// -// params +// ParamWidgets and other components //////////////////// +/** A Widget that exists on a Panel and interacts with a Module */ +struct Component : OpaqueWidget { + Module *module = NULL; + + template + static T *create(Vec pos, Module *module) { + T *o = new T(); + o->box.pos = pos; + o->module = module; + return o; + } +}; + struct CircularShadow : TransparentWidget { float blur = 0.0; void draw(NVGcontext *vg) override; }; -struct ParamWidget : OpaqueWidget, QuantityWidget { - Module *module = NULL; +/** A Component which has control over a Param (defined in engine.hpp) */ +struct Parameter : Component, QuantityWidget { int paramId; /** Used to momentarily disable value randomization To permanently disable or change randomization behavior, override the randomize() method instead of changing this. @@ -225,10 +238,9 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { void onMouseDown(EventMouseDown &e) override; void onChange(EventChange &e) override; - template + template static T *create(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { - T *o = Widget::create(pos); - o->module = module; + T *o = Component::create(pos, module); o->paramId = paramId; o->setLimits(minValue, maxValue); o->setDefaultValue(defaultValue); @@ -236,8 +248,11 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { } }; +/** Deprecated name of Parameter */ +typedef Parameter ParamWidget; + /** Implements vertical dragging behavior for ParamWidgets */ -struct Knob : ParamWidget { +struct Knob : Parameter { /** Snap to nearest integer while dragging */ bool snap = false; /** Multiplier for mouse movement to adjust knob value */ @@ -249,16 +264,16 @@ struct Knob : ParamWidget { void onDragEnd(EventDragEnd &e) override; }; -struct SpriteKnob : virtual Knob, SpriteWidget { +/** Deprecated */ +struct SpriteKnob : Knob, SpriteWidget { int minIndex, maxIndex, spriteCount; void step() override; }; /** A knob which rotates an SVG and caches it in a framebuffer */ -struct SVGKnob : virtual Knob, FramebufferWidget { +struct SVGKnob : Knob, FramebufferWidget { /** Angles in radians */ float minAngle, maxAngle; - /** Not owned */ TransformWidget *tw; SVGWidget *sw; @@ -268,6 +283,9 @@ struct SVGKnob : virtual Knob, FramebufferWidget { void onChange(EventChange &e) override; }; +/** Behaves like a knob but linearly moves an SVGWidget between two points. +Can be used for horizontal or vertical linear faders. +*/ struct SVGFader : Knob, FramebufferWidget { /** Intermediate positions will be interpolated between these positions */ Vec minHandlePos, maxHandlePos; @@ -280,14 +298,9 @@ struct SVGFader : Knob, FramebufferWidget { void onChange(EventChange &e) override; }; -struct Switch : ParamWidget { -}; - -struct SVGSwitch : virtual Switch, FramebufferWidget { +struct SVGSwitch : virtual Parameter, FramebufferWidget { std::vector> frames; - /** Not owned */ SVGWidget *sw; - SVGSwitch(); /** Adds an SVG file to represent the next switch position */ void addFrame(std::shared_ptr svg); @@ -295,29 +308,33 @@ struct SVGSwitch : virtual Switch, FramebufferWidget { }; /** A switch that cycles through each mechanical position */ -struct ToggleSwitch : virtual Switch { - void onDragStart(EventDragStart &e) override { - // Cycle through values - // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. - if (value >= maxValue) - setValue(minValue); - else - setValue(value + 1.0); - } +struct ToggleSwitch : virtual Parameter { + void onDragStart(EventDragStart &e) override; }; -/** A switch that is turned on when held */ -struct MomentarySwitch : virtual Switch { +/** A switch that is turned on when held and turned off when released. +Consider using SVGButton if the switch simply changes the state of your Module when clicked. +*/ +struct MomentarySwitch : virtual Parameter { /** Don't randomize state */ void randomize() override {} - void onDragStart(EventDragStart &e) override { - setValue(maxValue); - EventAction eAction; - onAction(eAction); - } - void onDragEnd(EventDragEnd &e) override { - setValue(minValue); - } + void onDragStart(EventDragStart &e) override; + void onDragEnd(EventDragEnd &e) override; +}; + +/** A Component with a default (up) and active (down) state when clicked. +Does not modify a Param, simply calls onAction() of a subclass. +*/ +struct SVGButton : Component, FramebufferWidget { + Module *module = NULL; + std::shared_ptr defaultSVG; + std::shared_ptr activeSVG; + SVGWidget *sw; + SVGButton(); + /** If `activeSVG` is NULL, `defaultSVG` is used as the active state instead. */ + void setSVGs(std::shared_ptr defaultSVG, std::shared_ptr activeSVG); + void onDragStart(EventDragStart &e) override; + void onDragEnd(EventDragEnd &e) override; }; //////////////////// diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index fb151c6a..85bbb3fd 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -452,6 +452,26 @@ struct TinyLight : BASE { } }; +/** A light to displayed over PB61303. Must add a color by subclassing or templating. */ +template +struct LEDBezelLight : BASE { + LEDBezelLight() { + this->bgColor = COLOR_BLACK_TRANSPARENT; + this->box.size = mm2px(Vec(6.0, 6.0)); + } +}; + +/** A light to displayed over PB61303. Must add a color by subclassing or templating. +Don't add this as a child of the PB61303 itself. Instead, just place it over it as a sibling in the scene graph, offset by mm2px(Vec(0.5, 0.5)). +*/ +template +struct PB61303Light : BASE { + PB61303Light() { + this->bgColor = COLOR_BLACK_TRANSPARENT; + this->box.size = mm2px(Vec(9.0, 9.0)); + } +}; + //////////////////// // Switches and Buttons @@ -515,18 +535,29 @@ struct BefacoPush : SVGSwitch, MomentarySwitch { } }; +struct LEDBezel : SVGSwitch, MomentarySwitch { + LEDBezel() { + addFrame(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg"))); + } +}; + struct PB61303 : SVGSwitch, MomentarySwitch { PB61303() { addFrame(SVG::load(assetGlobal("res/ComponentLibrary/PB61303.svg"))); } }; -struct LEDBezel : SVGSwitch, MomentarySwitch { - LEDBezel() { - addFrame(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg"))); +struct PB61303Button : SVGButton { + PB61303Button() { + setSVGs(SVG::load(assetGlobal("res/ComponentLibrary/PB61303.svg")), NULL); } }; +struct LEDBezelButton : SVGButton { + LEDBezelButton() { + setSVGs(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg")), NULL); + } +}; //////////////////// // Misc diff --git a/res/ComponentLibrary/PB61303.svg b/res/ComponentLibrary/PB61303.svg index 42ac4a99..e1efe7df 100644 --- a/res/ComponentLibrary/PB61303.svg +++ b/res/ComponentLibrary/PB61303.svg @@ -7,209 +7,17 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="10.000438mm" height="10.000403mm" viewBox="0 0 10.000438 10.000403" version="1.1" - id="svg81475" + id="svg27765" inkscape:version="0.92.2 5c3e80d, 2017-08-06" sodipodi:docname="PB61303.svg"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="defs27759" /> + id="metadata27762"> @@ -248,16 +56,11 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-57.743829,-79.577179)"> + transform="translate(-52.452162,-82.600989)"> - + style="fill:#211e1e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" + d="m 62.4526,87.601896 c 0,2.760204 -2.239327,4.999496 -4.999531,4.999496 -2.76158,0 -5.000907,-2.239292 -5.000907,-4.999496 0,-2.761615 2.239327,-5.000907 5.000907,-5.000907 2.760204,0 4.999531,2.239292 4.999531,5.000907" + id="path26168" /> diff --git a/src/Core/QuadMIDIToCVInterface.cpp b/src/Core/QuadMIDIToCVInterface.cpp index 99e0f210..48e13f09 100644 --- a/src/Core/QuadMIDIToCVInterface.cpp +++ b/src/Core/QuadMIDIToCVInterface.cpp @@ -43,6 +43,7 @@ struct QuadMIDIToCVInterface : Module { uint8_t notes[4]; bool gates[4]; bool pedal; + int rotateIndex; QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), heldNotes(128) { onReset(); @@ -71,6 +72,7 @@ struct QuadMIDIToCVInterface : Module { gates[i] = false; } pedal = false; + rotateIndex = 0; } void pressNote(uint8_t note) { @@ -84,17 +86,23 @@ struct QuadMIDIToCVInterface : Module { // Set notes and gates switch (polyMode) { case ROTATE_MODE: { - } break; + case RESET_MODE: { } break; + case REASSIGN_MODE: { } break; - case UNISON_MODE: { + case UNISON_MODE: { + for (int i = 0; i < 4; i++) { + notes[i] = note; + gates[i] = true; + } } break; + default: break; } } @@ -105,18 +113,41 @@ struct QuadMIDIToCVInterface : Module { if (it != heldNotes.end()) heldNotes.erase(it); // Hold note if pedal is pressed - // if (pedal) - // return; - // // Set last note - // if (!heldNotes.empty()) { - // auto it2 = heldNotes.end(); - // it2--; - // lastNote = *it2; - // gate = true; - // } - // else { - // gate = false; - // } + if (pedal) + return; + // Set last note + switch (polyMode) { + case ROTATE_MODE: { + + } break; + + case RESET_MODE: { + + } break; + + case REASSIGN_MODE: { + + } break; + + case UNISON_MODE: { + if (!heldNotes.empty()) { + auto it2 = heldNotes.end(); + it2--; + for (int i = 0; i < 4; i++) { + notes[i] = *it2; + gates[i] = true; + } + } + else { + for (int i = 0; i < 4; i++) { + gates[i] = false; + } + } + } break; + + default: break; + } + } void pressPedal() { diff --git a/src/app/LightWidget.cpp b/src/app/LightWidget.cpp index 7b4443c3..1300b432 100644 --- a/src/app/LightWidget.cpp +++ b/src/app/LightWidget.cpp @@ -34,7 +34,7 @@ void LightWidget::drawHalo(NVGcontext *vg) { nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); NVGpaint paint; - NVGcolor icol = colorMult(color, 0.15); + NVGcolor icol = colorMult(color, 0.25); NVGcolor ocol = nvgRGB(0, 0, 0); paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); nvgFillPaint(vg, paint); diff --git a/src/app/MomentarySwitch.cpp b/src/app/MomentarySwitch.cpp new file mode 100644 index 00000000..7eeed43c --- /dev/null +++ b/src/app/MomentarySwitch.cpp @@ -0,0 +1,18 @@ +#include "app.hpp" + + +namespace rack { + + +void MomentarySwitch::onDragStart(EventDragStart &e) { + setValue(maxValue); + EventAction eAction; + onAction(eAction); +} + +void MomentarySwitch::onDragEnd(EventDragEnd &e) { + setValue(minValue); +} + + +} // namespace rack diff --git a/src/app/ParamWidget.cpp b/src/app/Parameter.cpp similarity index 75% rename from src/app/ParamWidget.cpp rename to src/app/Parameter.cpp index 4ddf02eb..02690e2d 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/Parameter.cpp @@ -5,29 +5,29 @@ namespace rack { -json_t *ParamWidget::toJson() { +json_t *Parameter::toJson() { json_t *rootJ = json_object(); json_object_set_new(rootJ, "paramId", json_integer(paramId)); json_object_set_new(rootJ, "value", json_real(value)); return rootJ; } -void ParamWidget::fromJson(json_t *rootJ) { +void Parameter::fromJson(json_t *rootJ) { json_t *valueJ = json_object_get(rootJ, "value"); if (valueJ) setValue(json_number_value(valueJ)); } -void ParamWidget::reset() { +void Parameter::reset() { setValue(defaultValue); } -void ParamWidget::randomize() { +void Parameter::randomize() { if (randomizable) setValue(rescale(randomUniform(), 0.0, 1.0, minValue, maxValue)); } -void ParamWidget::onMouseDown(EventMouseDown &e) { +void Parameter::onMouseDown(EventMouseDown &e) { if (e.button == 1) { setValue(defaultValue); } @@ -35,7 +35,7 @@ void ParamWidget::onMouseDown(EventMouseDown &e) { e.target = this; } -void ParamWidget::onChange(EventChange &e) { +void Parameter::onChange(EventChange &e) { if (!module) return; diff --git a/src/app/SVGButton.cpp b/src/app/SVGButton.cpp new file mode 100644 index 00000000..68e4bb23 --- /dev/null +++ b/src/app/SVGButton.cpp @@ -0,0 +1,32 @@ +#include "app.hpp" + + +namespace rack { + + +SVGButton::SVGButton() { + sw = new SVGWidget(); + addChild(sw); +} + +void SVGButton::setSVGs(std::shared_ptr defaultSVG, std::shared_ptr activeSVG) { + sw->setSVG(defaultSVG); + box.size = sw->box.size; + this->defaultSVG = defaultSVG; + this->activeSVG = activeSVG ? activeSVG : defaultSVG; +} + +void SVGButton::onDragStart(EventDragStart &e) { + EventAction eAction; + onAction(eAction); + sw->setSVG(activeSVG); + dirty = true; +} + +void SVGButton::onDragEnd(EventDragEnd &e) { + sw->setSVG(defaultSVG); + dirty = true; +} + + +} // namespace rack diff --git a/src/app/SVGSwitch.cpp b/src/app/SVGSwitch.cpp index 3d163604..d399cd06 100644 --- a/src/app/SVGSwitch.cpp +++ b/src/app/SVGSwitch.cpp @@ -24,7 +24,7 @@ void SVGSwitch::onChange(EventChange &e) { int index = clamp((int) roundf(valueScaled), 0, frames.size() - 1); sw->setSVG(frames[index]); dirty = true; - Switch::onChange(e); + ParamWidget::onChange(e); } diff --git a/src/app/ToggleSwitch.cpp b/src/app/ToggleSwitch.cpp new file mode 100644 index 00000000..cb3c9404 --- /dev/null +++ b/src/app/ToggleSwitch.cpp @@ -0,0 +1,17 @@ +#include "app.hpp" + + +namespace rack { + + +void ToggleSwitch::onDragStart(EventDragStart &e) { + // Cycle through values + // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. + if (value >= maxValue) + setValue(minValue); + else + setValue(value + 1.0); +} + + +} // namespace rack diff --git a/src/main.cpp b/src/main.cpp index 1758f2c6..9443ebeb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,7 +20,7 @@ int main(int argc, char* argv[]) { gLogFile = fopen(logFilename.c_str(), "w"); #endif - info("Rack v%s", gApplicationVersion.c_str()); + info("Rack %s", gApplicationVersion.c_str()); { char *cwd = getcwd(NULL, 0);