diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index 6cb66dd3..c5071dae 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -39,6 +39,174 @@ static const NVGcolor SCHEME_PURPLE = nvgRGB(0xd5, 0x2b, 0xed); static const NVGcolor SCHEME_LIGHT_GRAY = nvgRGB(0xe6, 0xe6, 0xe6); static const NVGcolor SCHEME_DARK_GRAY = nvgRGB(0x17, 0x17, 0x17); + +//////////////////// +// Lights +//////////////////// + +/* +Many of these classes use CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). + +To use a red light with its default base class for example, use `RedLight` or `TRedLight<>`. (They are synonymous.) + +Use the `TBase` template argument if you want a different base class. +E.g. `RectangleLight` + +Although this paradigm might seem confusing at first, it ends up being extremely simple in your plugin code and perfect for "decorating" your classes with appearance traits and behavioral properties. +For example, need a slider with a green LED? Just use + + createLightParamCentered>(...) +*/ + +template +struct TGrayModuleLightWidget : TBase { + TGrayModuleLightWidget() { + this->bgColor = nvgRGB(0x5a, 0x5a, 0x5a); + this->borderColor = nvgRGBA(0, 0, 0, 0x60); + } +}; +typedef TGrayModuleLightWidget<> GrayModuleLightWidget; + +template +struct TRedLight : TBase { + TRedLight() { + this->addBaseColor(SCHEME_RED); + } +}; +typedef TRedLight<> RedLight; + +template +struct TGreenLight : TBase { + TGreenLight() { + this->addBaseColor(SCHEME_GREEN); + } +}; +typedef TGreenLight<> GreenLight; + +template +struct TYellowLight : TBase { + TYellowLight() { + this->addBaseColor(SCHEME_YELLOW); + } +}; +typedef TYellowLight<> YellowLight; + +template +struct TBlueLight : TBase { + TBlueLight() { + this->addBaseColor(SCHEME_BLUE); + } +}; +typedef TBlueLight<> BlueLight; + +template +struct TWhiteLight : TBase { + TWhiteLight() { + this->addBaseColor(SCHEME_WHITE); + } +}; +typedef TWhiteLight<> WhiteLight; + +/** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ +template +struct TGreenRedLight : TBase { + TGreenRedLight() { + this->addBaseColor(SCHEME_GREEN); + this->addBaseColor(SCHEME_RED); + } +}; +typedef TGreenRedLight<> GreenRedLight; + +template +struct TRedGreenBlueLight : TBase { + TRedGreenBlueLight() { + this->addBaseColor(SCHEME_RED); + this->addBaseColor(SCHEME_GREEN); + this->addBaseColor(SCHEME_BLUE); + } +}; +typedef TRedGreenBlueLight<> RedGreenBlueLight; + +/** Based on the size of 5mm LEDs */ +template +struct LargeLight : TBase { + LargeLight() { + this->box.size = app::mm2px(math::Vec(5.179, 5.179)); + } +}; + +/** Based on the size of 3mm LEDs */ +template +struct MediumLight : TBase { + MediumLight() { + this->box.size = app::mm2px(math::Vec(3.176, 3.176)); + } +}; + +/** Based on the size of 2mm LEDs */ +template +struct SmallLight : TBase { + SmallLight() { + this->box.size = app::mm2px(math::Vec(2.176, 2.176)); + } +}; + +/** Based on the size of 1mm LEDs */ +template +struct TinyLight : TBase { + TinyLight() { + this->box.size = app::mm2px(math::Vec(1.088, 1.088)); + } +}; + +template +struct RectangleLight : TBase { + void drawLight(const widget::Widget::DrawArgs& args) override { + nvgBeginPath(args.vg); + nvgRect(args.vg, 0, 0, this->box.size.x, this->box.size.y); + + // Background + if (this->bgColor.a > 0.0) { + nvgFillColor(args.vg, this->bgColor); + nvgFill(args.vg); + } + + // Foreground + if (this->color.a > 0.0) { + nvgFillColor(args.vg, this->color); + nvgFill(args.vg); + } + + // Border + if (this->borderColor.a > 0.0) { + nvgStrokeWidth(args.vg, 0.5); + nvgStrokeColor(args.vg, this->borderColor); + nvgStroke(args.vg); + } + } +}; + +/** A light for displaying on top of PB61303. Must add a color by subclassing or templating. */ +template +struct LEDBezelLight : TBase { + LEDBezelLight() { + this->bgColor = color::BLACK_TRANSPARENT; + this->box.size = app::mm2px(math::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 app::mm2px(math::Vec(0.5, 0.5)). +*/ +template +struct PB61303Light : TBase { + PB61303Light() { + this->bgColor = color::BLACK_TRANSPARENT; + this->box.size = app::mm2px(math::Vec(9.0, 9.0)); + } +}; + + //////////////////// // Knobs //////////////////// @@ -390,134 +558,75 @@ struct LEDSliderWhite : LEDSlider { } }; -//////////////////// -// Ports -//////////////////// - -struct PJ301MPort : app::SvgPort { - PJ301MPort() { - setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ301M.svg"))); +struct LEDSliderHorizontal : app::SvgSlider { + LEDSliderHorizontal() { + horizontal = true; + maxHandlePos = app::mm2px(math::Vec(22.078, 0.738).plus(math::Vec(0, 2))); + minHandlePos = app::mm2px(math::Vec(0.738, 0.738).plus(math::Vec(0, 2))); + setBackgroundSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSliderHorizontal.svg"))); } }; -struct PJ3410Port : app::SvgPort { - PJ3410Port() { - setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ3410.svg"))); - } -}; +template +struct LightSlider : TBase { + app::ModuleLightWidget* light; -struct CL1362Port : app::SvgPort { - CL1362Port() { - setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/CL1362.svg"))); + LightSlider() { + light = new RectangleLight; + this->addChild(light); } -}; - -//////////////////// -// Lights -//////////////////// -struct GrayModuleLightWidget : app::ModuleLightWidget { - GrayModuleLightWidget() { - bgColor = nvgRGB(0x5a, 0x5a, 0x5a); - borderColor = nvgRGBA(0, 0, 0, 0x60); + void setFirstLightId(int firstLightId) { + if (this->paramQuantity) + light->module = this->paramQuantity->module; + light->firstLightId = firstLightId; } -}; - -struct RedLight : GrayModuleLightWidget { - RedLight() { - addBaseColor(SCHEME_RED); - } -}; - -struct GreenLight : GrayModuleLightWidget { - GreenLight() { - addBaseColor(SCHEME_GREEN); - } -}; - -struct YellowLight : GrayModuleLightWidget { - YellowLight() { - addBaseColor(SCHEME_YELLOW); - } -}; -struct BlueLight : GrayModuleLightWidget { - BlueLight() { - addBaseColor(SCHEME_BLUE); + void step() override { + TBase::step(); + // Move center of light to center of handle + light->box.pos = this->handle->box.pos + .plus(this->handle->box.size.div(2)) + .minus(light->box.size.div(2)); } }; -struct WhiteLight : GrayModuleLightWidget { - WhiteLight() { - addBaseColor(SCHEME_WHITE); +template +struct LEDLightSlider : LightSlider { + LEDLightSlider() { + this->setHandleSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSliderHandle.svg"))); + this->light->box.size = app::mm2px(math::Vec(1.524, 3.276)); } }; -/** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ -struct GreenRedLight : GrayModuleLightWidget { - GreenRedLight() { - addBaseColor(SCHEME_GREEN); - addBaseColor(SCHEME_RED); - } -}; - -struct RedGreenBlueLight : GrayModuleLightWidget { - RedGreenBlueLight() { - addBaseColor(SCHEME_RED); - addBaseColor(SCHEME_GREEN); - addBaseColor(SCHEME_BLUE); - } -}; - -/** Based on the size of 5mm LEDs */ -template -struct LargeLight : BASE { - LargeLight() { - this->box.size = app::mm2px(math::Vec(5.179, 5.179)); +template +struct LEDLightSliderHorizontal : LightSlider { + LEDLightSliderHorizontal() { + this->setHandleSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/LEDSliderHorizontalHandle.svg"))); + this->light->box.size = app::mm2px(math::Vec(3.276, 1.524)); } }; -/** Based on the size of 3mm LEDs */ -template -struct MediumLight : BASE { - MediumLight() { - this->box.size = app::mm2px(math::Vec(3.176, 3.176)); - } -}; -/** Based on the size of 2mm LEDs */ -template -struct SmallLight : BASE { - SmallLight() { - this->box.size = app::mm2px(math::Vec(2.176, 2.176)); - } -}; +//////////////////// +// Ports +//////////////////// -/** Based on the size of 1mm LEDs */ -template -struct TinyLight : BASE { - TinyLight() { - this->box.size = app::mm2px(math::Vec(1.088, 1.088)); +struct PJ301MPort : app::SvgPort { + PJ301MPort() { + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ301M.svg"))); } }; -/** A light for displaying on top of PB61303. Must add a color by subclassing or templating. */ -template -struct LEDBezelLight : BASE { - LEDBezelLight() { - this->bgColor = color::BLACK_TRANSPARENT; - this->box.size = app::mm2px(math::Vec(6.0, 6.0)); +struct PJ3410Port : app::SvgPort { + PJ3410Port() { + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/PJ3410.svg"))); } }; -/** 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 app::mm2px(math::Vec(0.5, 0.5)). -*/ -template -struct PB61303Light : BASE { - PB61303Light() { - this->bgColor = color::BLACK_TRANSPARENT; - this->box.size = app::mm2px(math::Vec(9.0, 9.0)); +struct CL1362Port : app::SvgPort { + CL1362Port() { + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/CL1362.svg"))); } }; @@ -595,6 +704,24 @@ struct LEDBezel : app::SvgSwitch { } }; +template +struct LEDLightBezel : LEDBezel { + app::ModuleLightWidget* light; + + LEDLightBezel() { + light = new LEDBezelLight; + // Move center of light to center of box + light->box.pos = box.size.div(2).minus(light->box.size.div(2)); + addChild(light); + } + + void setFirstLightId(int firstLightId) { + if (paramQuantity) + light->module = paramQuantity->module; + light->firstLightId = firstLightId; + } +}; + struct PB61303 : app::SvgSwitch { PB61303() { momentary = true; diff --git a/include/helpers.hpp b/include/helpers.hpp index fb26c1c3..ebde5c3a 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -124,6 +124,21 @@ TModuleLightWidget* createLightCentered(math::Vec pos, engine::Module* module, i return o; } +/** Creates a param with a light and calls setFirstLightId() on it. */ +template +TParamWidget* createLightParam(math::Vec pos, engine::Module* module, int paramId, int firstLightId) { + TParamWidget* o = createParam(pos, module, paramId); + o->setFirstLightId(firstLightId); + return o; +} + +template +TParamWidget* createLightParamCentered(math::Vec pos, engine::Module* module, int paramId, int firstLightId) { + TParamWidget* o = createParamCentered(pos, module, paramId); + o->setFirstLightId(firstLightId); + return o; +} + template TMenuLabel * createMenuLabel(std::string text) { TMenuLabel* o = new TMenuLabel; diff --git a/res/ComponentLibrary/LEDSliderHandle.svg b/res/ComponentLibrary/LEDSliderHandle.svg new file mode 100644 index 00000000..737514c0 --- /dev/null +++ b/res/ComponentLibrary/LEDSliderHandle.svg @@ -0,0 +1,66 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderHorizontal.svg b/res/ComponentLibrary/LEDSliderHorizontal.svg new file mode 100644 index 00000000..fac8fc37 --- /dev/null +++ b/res/ComponentLibrary/LEDSliderHorizontal.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderHorizontalHandle.svg b/res/ComponentLibrary/LEDSliderHorizontalHandle.svg new file mode 100644 index 00000000..3673923f --- /dev/null +++ b/res/ComponentLibrary/LEDSliderHorizontalHandle.svg @@ -0,0 +1,66 @@ + + + + + + + + + + image/svg+xml + + + + + + + + +