diff --git a/include/app/LightWidget.hpp b/include/app/LightWidget.hpp index deb29a05..5c6bcac8 100644 --- a/include/app/LightWidget.hpp +++ b/include/app/LightWidget.hpp @@ -13,6 +13,7 @@ struct LightWidget : widget::TransparentWidget { NVGcolor borderColor = nvgRGBA(0, 0, 0, 0); void draw(const DrawArgs& args) override; + virtual void drawBackground(const DrawArgs& args); virtual void drawLight(const DrawArgs& args); virtual void drawHalo(const DrawArgs& args); }; diff --git a/include/app/SvgSlider.hpp b/include/app/SvgSlider.hpp index dce80d4a..95fd3994 100644 --- a/include/app/SvgSlider.hpp +++ b/include/app/SvgSlider.hpp @@ -23,6 +23,8 @@ struct SvgSlider : app::SliderKnob { void setBackgroundSvg(std::shared_ptr svg); void setHandleSvg(std::shared_ptr svg); void onChange(const ChangeEvent& e) override; + void setHandlePos(math::Vec minHandlePos, math::Vec maxHandlePos); + void setHandlePosCentered(math::Vec minHandlePosCentered, math::Vec maxHandlePosCentered); DEPRECATED void setBackgroundSVG(std::shared_ptr svg) { setBackgroundSvg(svg); diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index 2b9e3f80..e456aa6b 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -53,7 +53,7 @@ Many of these classes use CRTP (https://en.wikipedia.org/wiki/Curiously_recurrin To use a red light with its default base class for example, use `RedLight` or `TRedLight<>`. (They are synonymous.) -Use the `Base` template argument if you want a different base class. +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. @@ -62,8 +62,8 @@ For example, need a slider with a green LED? Just use createLightParamCentered>(...) */ -template -struct TSvgLight : Base { +template +struct TSvgLight : TBase { widget::FramebufferWidget* fb; widget::SvgWidget* sw; @@ -83,8 +83,8 @@ struct TSvgLight : Base { }; typedef TSvgLight<> SvgLight; -template -struct TGrayModuleLightWidget : Base { +template +struct TGrayModuleLightWidget : TBase { TGrayModuleLightWidget() { this->bgColor = nvgRGBA(0x33, 0x33, 0x33, 0xff); this->borderColor = nvgRGBA(0, 0, 0, 53); @@ -92,40 +92,40 @@ struct TGrayModuleLightWidget : Base { }; typedef TGrayModuleLightWidget<> GrayModuleLightWidget; -template -struct TRedLight : Base { +template +struct TRedLight : TBase { TRedLight() { this->addBaseColor(SCHEME_RED); } }; typedef TRedLight<> RedLight; -template -struct TGreenLight : Base { +template +struct TGreenLight : TBase { TGreenLight() { this->addBaseColor(SCHEME_GREEN); } }; typedef TGreenLight<> GreenLight; -template -struct TYellowLight : Base { +template +struct TYellowLight : TBase { TYellowLight() { this->addBaseColor(SCHEME_YELLOW); } }; typedef TYellowLight<> YellowLight; -template -struct TBlueLight : Base { +template +struct TBlueLight : TBase { TBlueLight() { this->addBaseColor(SCHEME_BLUE); } }; typedef TBlueLight<> BlueLight; -template -struct TWhiteLight : Base { +template +struct TWhiteLight : TBase { TWhiteLight() { this->addBaseColor(SCHEME_WHITE); } @@ -133,8 +133,8 @@ struct TWhiteLight : Base { typedef TWhiteLight<> WhiteLight; /** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ -template -struct TGreenRedLight : Base { +template +struct TGreenRedLight : TBase { TGreenRedLight() { this->addBaseColor(SCHEME_GREEN); this->addBaseColor(SCHEME_RED); @@ -142,8 +142,8 @@ struct TGreenRedLight : Base { }; typedef TGreenRedLight<> GreenRedLight; -template -struct TRedGreenBlueLight : Base { +template +struct TRedGreenBlueLight : TBase { TRedGreenBlueLight() { this->addBaseColor(SCHEME_RED); this->addBaseColor(SCHEME_GREEN); @@ -153,40 +153,42 @@ struct TRedGreenBlueLight : Base { typedef TRedGreenBlueLight<> RedGreenBlueLight; /** Based on the size of 5mm LEDs */ -template -struct LargeLight : TSvgLight { +template +struct LargeLight : TSvgLight { LargeLight() { this->setSvg(Svg::load(asset::system("res/ComponentLibrary/LargeLight.svg"))); } }; /** Based on the size of 3mm LEDs */ -template -struct MediumLight : TSvgLight { +template +struct MediumLight : TSvgLight { MediumLight() { this->setSvg(Svg::load(asset::system("res/ComponentLibrary/MediumLight.svg"))); } }; /** Based on the size of 2mm LEDs */ -template -struct SmallLight : TSvgLight { +template +struct SmallLight : TSvgLight { SmallLight() { this->setSvg(Svg::load(asset::system("res/ComponentLibrary/SmallLight.svg"))); } }; /** Based on the size of 1mm LEDs */ -template -struct TinyLight : TSvgLight { +template +struct TinyLight : TSvgLight { TinyLight() { this->setSvg(Svg::load(asset::system("res/ComponentLibrary/TinyLight.svg"))); } }; -template -struct RectangleLight : Base { - void drawLight(const widget::Widget::DrawArgs& args) override { +template +struct RectangleLight : TBase { + void drawBackground(const widget::Widget::DrawArgs& args) override { + // Derived from LightWidget::drawBackground() + nvgBeginPath(args.vg); nvgRect(args.vg, 0, 0, this->box.size.x, this->box.size.y); @@ -196,12 +198,6 @@ struct RectangleLight : Base { 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); @@ -209,11 +205,24 @@ struct RectangleLight : Base { nvgStroke(args.vg); } } + + void drawLight(const widget::Widget::DrawArgs& args) override { + // Derived from LightWidget::drawLight() + + // Foreground + if (this->color.a > 0.0) { + nvgBeginPath(args.vg); + nvgRect(args.vg, 0, 0, this->box.size.x, this->box.size.y); + + nvgFillColor(args.vg, this->color); + nvgFill(args.vg); + } + } }; /** A light for displaying on top of PB61303. Must add a color by subclassing or templating. */ -template -struct LEDBezelLight : Base { +template +struct LEDBezelLight : TBase { LEDBezelLight() { this->borderColor = color::BLACK_TRANSPARENT; this->bgColor = color::BLACK_TRANSPARENT; @@ -224,8 +233,8 @@ struct LEDBezelLight : Base { /** 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(math::Vec(0.5, 0.5)). */ -template -struct PB61303Light : Base { +template +struct PB61303Light : TBase { PB61303Light() { this->bgColor = color::BLACK_TRANSPARENT; this->box.size = mm2px(math::Vec(9.0, 9.0)); @@ -633,43 +642,52 @@ struct BefacoSlidePot : app::SvgSlider { struct LEDSlider : app::SvgSlider { LEDSlider() { - maxHandlePos = mm2px(math::Vec(0.738, 0.738).plus(math::Vec(2, 0))); - minHandlePos = mm2px(math::Vec(0.738, 22.078).plus(math::Vec(2, 0))); + // TODO Fix positions + setHandlePos( + mm2px(math::Vec(0.738, 22.078).plus(math::Vec(2, 0))), + mm2px(math::Vec(0.738, 0.738).plus(math::Vec(2, 0))) + ); setBackgroundSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSlider.svg"))); + setHandleSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderHandle.svg"))); } }; -/** API is unstable for LEDSlider. Will add a LightWidget later. */ +// TODO Modernize struct LEDSliderGreen : LEDSlider { LEDSliderGreen() { setHandleSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderGreenHandle.svg"))); } }; +// TODO Modernize struct LEDSliderRed : LEDSlider { LEDSliderRed() { setHandleSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderRedHandle.svg"))); } }; +// TODO Modernize struct LEDSliderYellow : LEDSlider { LEDSliderYellow() { setHandleSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderYellowHandle.svg"))); } }; +// TODO Modernize struct LEDSliderBlue : LEDSlider { LEDSliderBlue() { setHandleSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderBlueHandle.svg"))); } }; +// TODO Modernize struct LEDSliderWhite : LEDSlider { LEDSliderWhite() { setHandleSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderWhiteHandle.svg"))); } }; +// TODO Modernize struct LEDSliderHorizontal : app::SvgSlider { LEDSliderHorizontal() { horizontal = true; @@ -679,32 +697,37 @@ struct LEDSliderHorizontal : app::SvgSlider { } }; -template -struct LightSlider : Base { +template +struct LightSlider : TBase { app::ModuleLightWidget* light; LightSlider() { - light = new RectangleLight; + light = new TLightBase; this->addChild(light); } void step() override { - Base::step(); + 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)); + .plus(this->handle->box.size.div(2)) + .minus(light->box.size.div(2)); } }; -template -struct LEDLightSlider : LightSlider { - LEDLightSlider() { - this->setHandleSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderHandle.svg"))); - this->light->box.size = mm2px(math::Vec(1.524, 3.276)); +template +struct LEDSliderLight : RectangleLight> { + LEDSliderLight() { + this->setSvg(Svg::load(asset::system("res/ComponentLibrary/LEDSliderLight.svg"))); } }; +template +struct LEDLightSlider : LightSlider> { + LEDLightSlider() {} +}; + +// TODO Modernize template struct LEDLightSliderHorizontal : LightSlider { LEDLightSliderHorizontal() { diff --git a/src/app/LightWidget.cpp b/src/app/LightWidget.cpp index 818ba7d3..2b316aed 100644 --- a/src/app/LightWidget.cpp +++ b/src/app/LightWidget.cpp @@ -8,6 +8,22 @@ namespace app { void LightWidget::draw(const DrawArgs& args) { + drawBackground(args); + + // Child widgets + Widget::draw(args); + + // Dynamic light and halo + // Override tint from rack brightness adjustment + nvgGlobalAlpha(args.vg, 1.0); + // Use the formula `lightColor * (1 - dest) + dest` for blending + nvgGlobalCompositeBlendFunc(args.vg, NVG_ONE_MINUS_DST_COLOR, NVG_ONE); + drawLight(args); + drawHalo(args); +} + + +void LightWidget::drawBackground(const DrawArgs& args) { float radius = std::min(box.size.x, box.size.y) / 2.0; nvgBeginPath(args.vg); nvgCircle(args.vg, radius, radius, radius); @@ -24,21 +40,9 @@ void LightWidget::draw(const DrawArgs& args) { nvgStrokeColor(args.vg, borderColor); nvgStroke(args.vg); } - - // Child widgets - // TODO Upload new graphics instead of use this hack. - // nvgGlobalAlpha(args.vg, 0.5); - TransparentWidget::draw(args); - - // Dynamic light and halo - // Override tint from rack brightness adjustment - nvgGlobalAlpha(args.vg, 1.0); - // Use the formula `lightColor * (1 - dest) + dest` for blending - nvgGlobalCompositeBlendFunc(args.vg, NVG_ONE_MINUS_DST_COLOR, NVG_ONE); - drawLight(args); - drawHalo(args); } + void LightWidget::drawLight(const DrawArgs& args) { // Foreground if (color.a > 0.0) { @@ -51,6 +55,7 @@ void LightWidget::drawLight(const DrawArgs& args) { } } + void LightWidget::drawHalo(const DrawArgs& args) { // Don't draw halo if rendering in a framebuffer, e.g. screenshots or Module Browser if (args.fb) diff --git a/src/app/ModuleLightWidget.cpp b/src/app/ModuleLightWidget.cpp index 6239d067..57dee576 100644 --- a/src/app/ModuleLightWidget.cpp +++ b/src/app/ModuleLightWidget.cpp @@ -54,6 +54,8 @@ ModuleLightWidget::~ModuleLightWidget() { engine::Light* ModuleLightWidget::getLight(int colorId) { if (!module) return NULL; + if (firstLightId < 0) + return NULL; return &module->lights[firstLightId + colorId]; } @@ -61,6 +63,8 @@ engine::Light* ModuleLightWidget::getLight(int colorId) { engine::LightInfo* ModuleLightWidget::getLightInfo() { if (!module) return NULL; + if (firstLightId < 0) + return NULL; return module->lightInfos[firstLightId]; } @@ -92,8 +96,8 @@ void ModuleLightWidget::destroyTooltip() { void ModuleLightWidget::step() { std::vector brightnesses(baseColors.size()); - if (module) { - assert(module->lights.size() >= firstLightId + baseColors.size()); + if (module && firstLightId >= 0) { + assert((int) module->lights.size() >= firstLightId + (int) baseColors.size()); for (size_t i = 0; i < baseColors.size(); i++) { float b = module->lights[firstLightId + i].getBrightness(); diff --git a/src/app/SvgSlider.cpp b/src/app/SvgSlider.cpp index c50d0ac1..b4f25b0a 100644 --- a/src/app/SvgSlider.cpp +++ b/src/app/SvgSlider.cpp @@ -18,31 +18,45 @@ SvgSlider::SvgSlider() { speed = 2.0; } + void SvgSlider::setBackgroundSvg(std::shared_ptr svg) { background->setSvg(svg); - fb->box.size = background->box.size; box.size = background->box.size; + fb->box.size = background->box.size; + fb->setDirty(); } + void SvgSlider::setHandleSvg(std::shared_ptr svg) { handle->setSvg(svg); - handle->box.pos = maxHandlePos; - fb->dirty = true; + handle->box.pos = minHandlePos; + fb->setDirty(); } + void SvgSlider::onChange(const ChangeEvent& e) { engine::ParamQuantity* pq = getParamQuantity(); if (pq) { // Interpolate handle position float v = math::rescale(pq->getSmoothValue(), pq->getMinValue(), pq->getMaxValue(), 0.f, 1.f); - handle->box.pos = math::Vec( - math::rescale(v, 0.f, 1.f, minHandlePos.x, maxHandlePos.x), - math::rescale(v, 0.f, 1.f, minHandlePos.y, maxHandlePos.y)); - fb->dirty = true; + handle->box.pos = minHandlePos.crossfade(maxHandlePos, v); + fb->setDirty(); } ParamWidget::onChange(e); } +void SvgSlider::setHandlePos(math::Vec minHandlePos, math::Vec maxHandlePos) { + this->minHandlePos = minHandlePos; + this->maxHandlePos = maxHandlePos; +} + + +void SvgSlider::setHandlePosCentered(math::Vec minHandlePosCentered, math::Vec maxHandlePosCentered) { + this->minHandlePos = minHandlePosCentered.minus(handle->box.size.div(2)); + this->maxHandlePos = maxHandlePosCentered.minus(handle->box.size.div(2)); +} + + } // namespace app } // namespace rack