diff --git a/include/app/CableWidget.hpp b/include/app/CableWidget.hpp index 6c86438f..9c2efd50 100644 --- a/include/app/CableWidget.hpp +++ b/include/app/CableWidget.hpp @@ -10,7 +10,18 @@ namespace rack { namespace app { -struct PlugWidget; +struct PlugWidget : widget::Widget { + struct Internal; + Internal* internal; + + PlugWidget(); + ~PlugWidget(); + void step() override; + PRIVATE void setColor(NVGcolor color); + PRIVATE void setAngle(float angle); + PRIVATE void setPortWidget(PortWidget* portWidget); + PRIVATE void setTop(bool top); +}; struct CableWidget : widget::Widget { @@ -47,6 +58,8 @@ struct CableWidget : widget::Widget { void draw(const DrawArgs& args) override; void drawLayer(const DrawArgs& args, int layer) override; engine::Cable* releaseCable(); + void onAdd(const AddEvent& e) override; + void onRemove(const RemoveEvent& e) override; }; diff --git a/include/app/RackWidget.hpp b/include/app/RackWidget.hpp index 9208bf01..1b62c4e8 100644 --- a/include/app/RackWidget.hpp +++ b/include/app/RackWidget.hpp @@ -41,6 +41,7 @@ struct RackWidget : widget::OpaqueWidget { // Rack methods widget::Widget* getModuleContainer(); + widget::Widget* getPlugContainer(); widget::Widget* getCableContainer(); math::Vec getMousePos(); diff --git a/src/app/CableWidget.cpp b/src/app/CableWidget.cpp index 62a2ebd2..4b8aafff 100644 --- a/src/app/CableWidget.cpp +++ b/src/app/CableWidget.cpp @@ -33,7 +33,8 @@ struct PlugLight : componentlibrary::TRedGreenBlueLight { }; -struct PlugWidget : widget::Widget { +struct PlugWidget::Internal { + /** Initially pointing upward. */ float angle = 0.5f * M_PI; PortWidget* portWidget = NULL; @@ -45,75 +46,83 @@ struct PlugWidget : widget::Widget { widget::SvgWidget* plugPort; app::MultiLightWidget* plugLight; +}; + - PlugWidget() { - fb = new widget::FramebufferWidget; - addChild(fb); +PlugWidget::PlugWidget() { + internal = new Internal; - plugTransform = new widget::TransformWidget; - fb->addChild(plugTransform); + internal->fb = new widget::FramebufferWidget; + addChild(internal->fb); - plugTint = new TintWidget; - plugTransform->addChild(plugTint); + internal->plugTransform = new widget::TransformWidget; + internal->fb->addChild(internal->plugTransform); - plug = new widget::SvgWidget; - plug->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/Plug.svg"))); - plugTint->addChild(plug); - plugTransform->setSize(plug->getSize()); - plugTransform->setPosition(plug->getSize().mult(-0.5)); - plugTint->setSize(plug->getSize()); + internal->plugTint = new TintWidget; + internal->plugTransform->addChild(internal->plugTint); - plugPort = new widget::SvgWidget; - plugPort->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/PlugPort.svg"))); - plugPort->setPosition(plugPort->getSize().mult(-0.5)); - fb->addChild(plugPort); + internal->plug = new widget::SvgWidget; + internal->plug->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/Plug.svg"))); + internal->plugTint->addChild(internal->plug); + internal->plugTransform->setSize(internal->plug->getSize()); + internal->plugTransform->setPosition(internal->plug->getSize().mult(-0.5)); + internal->plugTint->setSize(internal->plug->getSize()); - plugLight = new PlugLight; - plugLight->setPosition(plugLight->getSize().mult(-0.5)); - addChild(plugLight); + internal->plugPort = new widget::SvgWidget; + internal->plugPort->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/PlugPort.svg"))); + internal->plugPort->setPosition(internal->plugPort->getSize().mult(-0.5)); + internal->fb->addChild(internal->plugPort); + + internal->plugLight = new PlugLight; + internal->plugLight->setPosition(internal->plugLight->getSize().mult(-0.5)); + addChild(internal->plugLight); + + setSize(internal->plug->getSize()); +} - setSize(plug->getSize()); - } - void step() override { - std::vector values(3); - if (portWidget && plugLight->isVisible()) { - engine::Port* port = portWidget->getPort(); - if (port) { - for (int i = 0; i < 3; i++) { - values[i] = port->plugLights[i].getBrightness(); - } +PlugWidget::~PlugWidget() { + delete internal; +} + +void PlugWidget::step() { + std::vector values(3); + if (internal->portWidget && internal->plugLight->isVisible()) { + engine::Port* port = internal->portWidget->getPort(); + if (port) { + for (int i = 0; i < 3; i++) { + values[i] = port->plugLights[i].getBrightness(); } } - plugLight->setBrightnesses(values); - - Widget::step(); } + internal->plugLight->setBrightnesses(values); - void setColor(NVGcolor color) { - if (color::isEqual(color, plugTint->color)) - return; - plugTint->color = color; - fb->setDirty(); - } + Widget::step(); +} - void setAngle(float angle) { - if (angle == this->angle) - return; - this->angle = angle; - plugTransform->identity(); - plugTransform->rotate(angle - 0.5f * M_PI, plug->getSize().div(2)); - fb->setDirty(); - } +void PlugWidget::setColor(NVGcolor color) { + if (color::isEqual(color, internal->plugTint->color)) + return; + internal->plugTint->color = color; + internal->fb->setDirty(); +} - void setPortWidget(PortWidget* portWidget) { - this->portWidget = portWidget; - } +void PlugWidget::setAngle(float angle) { + if (angle == internal->angle) + return; + internal->angle = angle; + internal->plugTransform->identity(); + internal->plugTransform->rotate(angle - 0.5f * M_PI, internal->plug->getSize().div(2)); + internal->fb->setDirty(); +} - void setTop(bool top) { - plugLight->setVisible(top); - } -}; +void PlugWidget::setPortWidget(PortWidget* portWidget) { + internal->portWidget = portWidget; +} + +void PlugWidget::setTop(bool top) { + internal->plugLight->setVisible(top); +} struct CableWidget::Internal { @@ -124,15 +133,15 @@ CableWidget::CableWidget() { internal = new Internal; color = color::BLACK_TRANSPARENT; - inputPlug = new PlugWidget; - addChild(inputPlug); - outputPlug = new PlugWidget; - addChild(outputPlug); + inputPlug = new PlugWidget; } CableWidget::~CableWidget() { + delete outputPlug; + delete inputPlug; + setCable(NULL); delete internal; } @@ -278,84 +287,81 @@ void CableWidget::step() { void CableWidget::draw(const DrawArgs& args) { - // Draw plugs - Widget::draw(args); + CableWidget::drawLayer(args, 0); } void CableWidget::drawLayer(const DrawArgs& args, int layer) { // Cable shadow and cable - if (layer == 2 || layer == 3) { - float opacity = settings::cableOpacity; - bool thick = false; - - if (isComplete()) { - engine::Output* output = &cable->outputModule->outputs[cable->outputId]; - // Increase thickness if output port is polyphonic - if (output->isPolyphonic()) { - thick = true; - } - - // Draw opaque if mouse is hovering over a connected port - Widget* hoveredWidget = APP->event->hoveredWidget; - if (outputPort == hoveredWidget || inputPort == hoveredWidget) { - opacity = 1.0; - } - // Draw translucent cable if not active (i.e. 0 channels) - else if (output->getChannels() == 0) { - opacity *= 0.5; - } - } - else { - // Draw opaque if the cable is incomplete - opacity = 1.0; + float opacity = settings::cableOpacity; + bool thick = false; + + if (isComplete()) { + engine::Output* output = &cable->outputModule->outputs[cable->outputId]; + // Increase thickness if output port is polyphonic + if (output->isPolyphonic()) { + thick = true; } - if (opacity <= 0.0) - return; - nvgAlpha(args.vg, std::pow(opacity, 1.5)); - - math::Vec outputPos = getOutputPos(); - math::Vec inputPos = getInputPos(); - - float thickness = thick ? 9.0 : 6.0; - - // The endpoints are off-center - math::Vec slump = getSlumpPos(outputPos, inputPos); - outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(13.0)); - inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(13.0)); - - nvgLineCap(args.vg, NVG_ROUND); - // Avoids glitches when cable is bent - nvgLineJoin(args.vg, NVG_ROUND); - - if (layer == 2) { - // Draw cable shadow - math::Vec shadowSlump = slump.plus(math::Vec(0, 30)); - nvgBeginPath(args.vg); - nvgMoveTo(args.vg, VEC_ARGS(outputPos)); - nvgQuadTo(args.vg, VEC_ARGS(shadowSlump), VEC_ARGS(inputPos)); - NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.10); - nvgStrokeColor(args.vg, shadowColor); - nvgStrokeWidth(args.vg, thickness - 1.0); - nvgStroke(args.vg); + // Draw opaque if mouse is hovering over a connected port + Widget* hoveredWidget = APP->event->hoveredWidget; + if (outputPort == hoveredWidget || inputPort == hoveredWidget) { + opacity = 1.0; } - else if (layer == 3) { - // Draw cable outline - nvgBeginPath(args.vg); - nvgMoveTo(args.vg, VEC_ARGS(outputPos)); - nvgQuadTo(args.vg, VEC_ARGS(slump), VEC_ARGS(inputPos)); - // nvgStrokePaint(args.vg, nvgLinearGradient(args.vg, VEC_ARGS(outputPos), VEC_ARGS(inputPos), color::mult(color, 0.5), color)); - nvgStrokeColor(args.vg, color::mult(color, 0.8)); - nvgStrokeWidth(args.vg, thickness); - nvgStroke(args.vg); - - // Draw cable - nvgStrokeColor(args.vg, color::mult(color, 0.95)); - nvgStrokeWidth(args.vg, thickness - 1.0); - nvgStroke(args.vg); + // Draw translucent cable if not active (i.e. 0 channels) + else if (output->getChannels() == 0) { + opacity *= 0.5; } } + else { + // Draw opaque if the cable is incomplete + opacity = 1.0; + } + + if (opacity <= 0.0) + return; + nvgAlpha(args.vg, std::pow(opacity, 1.5)); + + math::Vec outputPos = getOutputPos(); + math::Vec inputPos = getInputPos(); + + float thickness = thick ? 9.0 : 6.0; + + // The endpoints are off-center + math::Vec slump = getSlumpPos(outputPos, inputPos); + outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(13.0)); + inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(13.0)); + + nvgLineCap(args.vg, NVG_ROUND); + // Avoids glitches when cable is bent + nvgLineJoin(args.vg, NVG_ROUND); + + if (layer == -1) { + // Draw cable shadow + math::Vec shadowSlump = slump.plus(math::Vec(0, 30)); + nvgBeginPath(args.vg); + nvgMoveTo(args.vg, VEC_ARGS(outputPos)); + nvgQuadTo(args.vg, VEC_ARGS(shadowSlump), VEC_ARGS(inputPos)); + NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.10); + nvgStrokeColor(args.vg, shadowColor); + nvgStrokeWidth(args.vg, thickness - 1.0); + nvgStroke(args.vg); + } + else if (layer == 0) { + // Draw cable outline + nvgBeginPath(args.vg); + nvgMoveTo(args.vg, VEC_ARGS(outputPos)); + nvgQuadTo(args.vg, VEC_ARGS(slump), VEC_ARGS(inputPos)); + // nvgStrokePaint(args.vg, nvgLinearGradient(args.vg, VEC_ARGS(outputPos), VEC_ARGS(inputPos), color::mult(color, 0.5), color)); + nvgStrokeColor(args.vg, color::mult(color, 0.8)); + nvgStrokeWidth(args.vg, thickness); + nvgStroke(args.vg); + + // Draw cable + nvgStrokeColor(args.vg, color::mult(color, 0.95)); + nvgStrokeWidth(args.vg, thickness - 1.0); + nvgStroke(args.vg); + } Widget::drawLayer(args, layer); } @@ -368,5 +374,21 @@ engine::Cable* CableWidget::releaseCable() { } +void CableWidget::onAdd(const AddEvent& e) { + Widget* plugContainer = APP->scene->rack->getPlugContainer(); + plugContainer->addChild(outputPlug); + plugContainer->addChild(inputPlug); + Widget::onAdd(e); +} + + +void CableWidget::onRemove(const RemoveEvent& e) { + Widget* plugContainer = APP->scene->rack->getPlugContainer(); + plugContainer->removeChild(outputPlug); + plugContainer->removeChild(inputPlug); + Widget::onRemove(e); +} + + } // namespace app } // namespace rack diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 712341b1..93563048 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -26,6 +26,7 @@ namespace app { struct RackWidget::Internal { RailWidget* rail = NULL; widget::Widget* moduleContainer = NULL; + widget::Widget* plugContainer = NULL; widget::Widget* cableContainer = NULL; CableWidget* incompleteCable = NULL; int nextCableColorId = 0; @@ -74,7 +75,8 @@ struct ModuleContainer : widget::Widget { }; -struct CableContainer : widget::TransparentWidget { +/** Children PlugWidgets are owned by CableWidgets. */ +struct PlugContainer : widget::TransparentWidget { void draw(const DrawArgs& args) override { // Don't draw on layer 0 } @@ -84,17 +86,28 @@ struct CableContainer : widget::TransparentWidget { // Draw Plugs Widget::draw(args); - // Draw cable lights + // Draw plug lights nvgSave(args.vg); nvgGlobalTint(args.vg, color::WHITE); Widget::drawLayer(args, 1); nvgRestore(args.vg); + } + } +}; + + +struct CableContainer : widget::TransparentWidget { + void draw(const DrawArgs& args) override { + // Don't draw on layer 0 + } + void drawLayer(const DrawArgs& args, int layer) override { + if (layer == 3) { // Draw cable shadows - Widget::drawLayer(args, 2); + Widget::drawLayer(args, -1); // Draw cables - Widget::drawLayer(args, 3); + Widget::draw(args); } } }; @@ -109,6 +122,9 @@ RackWidget::RackWidget() { internal->moduleContainer = new ModuleContainer; addChild(internal->moduleContainer); + internal->plugContainer = new PlugContainer; + addChild(internal->plugContainer); + internal->cableContainer = new CableContainer; addChild(internal->cableContainer); } @@ -149,9 +165,12 @@ void RackWidget::draw(const DrawArgs& args) { // Tint all draws after this point nvgGlobalTint(args.vg, nvgRGBAf(b, b, b, 1)); - // Draw cables + // Draw plugs Widget::drawLayer(args, 2); + // Draw cables + Widget::drawLayer(args, 3); + // Draw selection rectangle if (internal->selecting) { nvgBeginPath(args.vg); @@ -221,6 +240,10 @@ widget::Widget* RackWidget::getModuleContainer() { return internal->moduleContainer; } +widget::Widget* RackWidget::getPlugContainer() { + return internal->plugContainer; +} + widget::Widget* RackWidget::getCableContainer() { return internal->cableContainer; } @@ -1372,6 +1395,7 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) { void RackWidget::clearCables() { internal->incompleteCable = NULL; + // Since cables manage plugs, all plugs are removed from plugContainer internal->cableContainer->clearChildren(); }