diff --git a/include/app.hpp b/include/app.hpp index 38c04fc0..f6b5dcac 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -83,13 +83,25 @@ struct WireWidget : OpaqueWidget { void drawPlugs(NVGcontext *vg); }; +struct WireContainer : TransparentWidget { + WireWidget *activeWire = NULL; + /** Takes ownership of `w` and adds it as a child if it isn't already */ + void setActiveWire(WireWidget *w); + /** "Drops" the wire onto the port, making an engine connection if successful */ + void commitActiveWire(); + void removeTopWire(Port *port); + void removeAllWires(Port *port); + /** Returns the most recently added wire connected to the given Port, i.e. the top of the stack */ + WireWidget *getTopWire(Port *port); + void draw(NVGcontext *vg); +}; + struct RackWidget : OpaqueWidget { FramebufferWidget *rails; // Only put ModuleWidgets in here Widget *moduleContainer; // Only put WireWidgets in here - Widget *wireContainer; - WireWidget *activeWire = NULL; + WireContainer *wireContainer; std::string lastPath; RackWidget(); @@ -249,20 +261,15 @@ struct MomentarySwitch : virtual Switch { struct Port : OpaqueWidget { enum PortType { - DEFAULT, INPUT, OUTPUT }; Module *module = NULL; - WireWidget *connectedWire = NULL; - PortType type = DEFAULT; + PortType type = INPUT; int portId; - Port(); ~Port(); - void disconnect(); - void draw(NVGcontext *vg); void onMouseDownOpaque(int button); void onDragEnd(); diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index e125f8f2..8f02cab6 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -97,10 +97,10 @@ void ModuleWidget::fromJson(json_t *rootJ) { void ModuleWidget::disconnect() { for (Port *input : inputs) { - input->disconnect(); + gRackWidget->wireContainer->removeAllWires(input); } for (Port *output : outputs) { - output->disconnect(); + gRackWidget->wireContainer->removeAllWires(output); } } diff --git a/src/app/Port.cpp b/src/app/Port.cpp index bc6f4c1a..d7a26848 100644 --- a/src/app/Port.cpp +++ b/src/app/Port.cpp @@ -3,109 +3,83 @@ namespace rack { -Port::Port() { - box.size = Vec(20, 20); -} - Port::~Port() { - disconnect(); - - if (gRackWidget->activeWire) { - if (gRackWidget->activeWire->hoveredInputPort == this) - gRackWidget->activeWire->hoveredInputPort = NULL; - if (gRackWidget->activeWire->hoveredOutputPort == this) - gRackWidget->activeWire->hoveredOutputPort = NULL; - } -} - -void Port::disconnect() { - if (connectedWire) { - gRackWidget->wireContainer->removeChild(connectedWire); - // On destruction, Wire automatically sets connectedWire to NULL - delete connectedWire; - } + gRackWidget->wireContainer->removeAllWires(this); } void Port::draw(NVGcontext *vg) { - if (gRackWidget->activeWire) { + WireWidget *activeWire = gRackWidget->wireContainer->activeWire; + if (activeWire) { // Dim the Port if the active wire cannot plug into this Port - if (type == INPUT ? gRackWidget->activeWire->inputPort : gRackWidget->activeWire->outputPort) + if (type == INPUT ? activeWire->inputPort : activeWire->outputPort) nvgGlobalAlpha(vg, 0.5); } } void Port::onMouseDownOpaque(int button) { if (button == 1) { - disconnect(); + gRackWidget->wireContainer->removeTopWire(this); + + // HACK + // Update hovered*Port of active wire if applicable + onDragEnter(NULL); } } void Port::onDragEnd() { - WireWidget *w = gRackWidget->activeWire; - assert(w); - w->updateWire(); - if (!w->wire) { - gRackWidget->wireContainer->removeChild(w); - delete w; - } - gRackWidget->activeWire = NULL; + gRackWidget->wireContainer->commitActiveWire(); } void Port::onDragStart() { - if (connectedWire) { - // Disconnect wire from this port, but set it as the active wire + // Try to grab wire on top of stack + WireWidget *wire = gRackWidget->wireContainer->getTopWire(this); + + if (wire) { + // Disconnect existing wire if (type == INPUT) - connectedWire->inputPort = NULL; + wire->inputPort = NULL; else - connectedWire->outputPort = NULL; - connectedWire->updateWire(); - gRackWidget->activeWire = connectedWire; - connectedWire = NULL; + wire->outputPort = NULL; + wire->updateWire(); } else { - connectedWire = new WireWidget(); + // Create a new wire + wire = new WireWidget(); if (type == INPUT) - connectedWire->inputPort = this; + wire->inputPort = this; else - connectedWire->outputPort = this; - gRackWidget->wireContainer->addChild(connectedWire); - gRackWidget->activeWire = connectedWire; + wire->outputPort = this; } + gRackWidget->wireContainer->setActiveWire(wire); } void Port::onDragDrop(Widget *origin) { - if (connectedWire) return; - if (gRackWidget->activeWire) { - if (type == INPUT) { - gRackWidget->activeWire->hoveredInputPort = NULL; - if (gRackWidget->activeWire->inputPort) return; - gRackWidget->activeWire->inputPort = this; - } - else { - gRackWidget->activeWire->hoveredOutputPort = NULL; - if (gRackWidget->activeWire->outputPort) return; - gRackWidget->activeWire->outputPort = this; - } - connectedWire = gRackWidget->activeWire; - } } void Port::onDragEnter(Widget *origin) { - if (connectedWire) return; - if (gRackWidget->activeWire) { + // Reject ports if this is an input port and something is already plugged into it + if (type == INPUT) { + WireWidget *topWire = gRackWidget->wireContainer->getTopWire(this); + if (topWire) + return; + } + + WireWidget *activeWire = gRackWidget->wireContainer->activeWire; + if (activeWire) { if (type == INPUT) - gRackWidget->activeWire->hoveredInputPort = this; + activeWire->hoveredInputPort = this; else - gRackWidget->activeWire->hoveredOutputPort = this; + activeWire->hoveredOutputPort = this; } } void Port::onDragLeave(Widget *origin) { - if (gRackWidget->activeWire) { + WireWidget *activeWire = gRackWidget->wireContainer->activeWire; + if (activeWire) { if (type == INPUT) - gRackWidget->activeWire->hoveredInputPort = NULL; + activeWire->hoveredInputPort = NULL; else - gRackWidget->activeWire->hoveredOutputPort = NULL; + activeWire->hoveredOutputPort = NULL; } } diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 1e6becb4..ffd43763 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -13,19 +13,6 @@ namespace rack { -struct WireContainer : TransparentWidget { - void draw(NVGcontext *vg) { - // Wire plugs - for (Widget *child : children) { - WireWidget *wire = dynamic_cast(child); - assert(wire); - wire->drawPlugs(vg); - } - - Widget::draw(vg); - } -}; - RackWidget::RackWidget() { rails = new FramebufferWidget(); RackRail *rail = new RackRail(); @@ -45,7 +32,7 @@ RackWidget::~RackWidget() { } void RackWidget::clear() { - activeWire = NULL; + wireContainer->activeWire = NULL; wireContainer->clearChildren(); moduleContainer->clearChildren(); lastPath = ""; @@ -253,8 +240,6 @@ void RackWidget::fromJson(json_t *rootJ) { WireWidget *wireWidget = new WireWidget(); wireWidget->outputPort = outputPort; wireWidget->inputPort = inputPort; - outputPort->connectedWire = wireWidget; - inputPort->connectedWire = wireWidget; wireWidget->updateWire(); // Add wire to rack wireContainer->addChild(wireWidget); diff --git a/src/app/WireContainer.cpp b/src/app/WireContainer.cpp new file mode 100644 index 00000000..dd614478 --- /dev/null +++ b/src/app/WireContainer.cpp @@ -0,0 +1,108 @@ +#include "app.hpp" + +namespace rack { + + +void WireContainer::setActiveWire(WireWidget *w) { + if (activeWire) { + removeChild(activeWire); + delete activeWire; + activeWire = NULL; + } + if (w) { + if (w->parent == NULL) + addChild(w); + activeWire = w; + } +} + +void WireContainer::commitActiveWire() { + if (!activeWire) + return; + + if (activeWire->hoveredInputPort) { + activeWire->inputPort = activeWire->hoveredInputPort; + activeWire->hoveredInputPort = NULL; + } + if (activeWire->hoveredOutputPort) { + activeWire->outputPort = activeWire->hoveredOutputPort; + activeWire->hoveredOutputPort = NULL; + } + activeWire->updateWire(); + + // Did it successfully connect? + if (activeWire->wire) { + // Make it permanent + activeWire = NULL; + } + else { + // Remove it + setActiveWire(NULL); + } +} + +void WireContainer::removeTopWire(Port *port) { + WireWidget *wire = getTopWire(port); + if (wire) { + removeChild(wire); + delete wire; + } +} + +void WireContainer::removeAllWires(Port *port) { + // As a convenience, de-hover the active wire so we don't attach them once it is dropped. + if (activeWire) { + if (activeWire->hoveredInputPort == port) + activeWire->hoveredInputPort = NULL; + if (activeWire->hoveredOutputPort == port) + activeWire->hoveredOutputPort = NULL; + } + + // Build a list of WireWidgets to delete + std::list wires; + + for (Widget *child : children) { + WireWidget *wire = dynamic_cast(child); + assert(wire); + if (wire->inputPort == port || wire->outputPort == port) { + if (activeWire == wire) { + activeWire = NULL; + } + // We can't delete from this list while we're iterating it, so add it to the deletion list. + wires.push_back(wire); + } + } + + // Once we're done building the list, actually delete them + for (WireWidget *wire : wires) { + removeChild(wire); + delete wire; + } +} + +WireWidget *WireContainer::getTopWire(Port *port) { + for (auto it = children.rbegin(); it != children.rend(); it++) { + WireWidget *wire = dynamic_cast(*it); + assert(wire); + // Ignore incomplete wires + if (!(wire->inputPort && wire->outputPort)) + continue; + if (wire->inputPort == port || wire->outputPort == port) + return wire; + } + return NULL; +} + +void WireContainer::draw(NVGcontext *vg) { + Widget::draw(vg); + + // Wire plugs + for (Widget *child : children) { + WireWidget *wire = dynamic_cast(child); + assert(wire); + wire->drawPlugs(vg); + } +} + + +} // namespace rack diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index 83ef38c7..eba4fac1 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -87,34 +87,32 @@ WireWidget::WireWidget() { } WireWidget::~WireWidget() { - if (outputPort) { - outputPort->connectedWire = NULL; - outputPort = NULL; - } - if (inputPort) { - inputPort->connectedWire = NULL; - inputPort = NULL; - } + outputPort = NULL; + inputPort = NULL; updateWire(); } void WireWidget::updateWire() { - if (wire) { - engineRemoveWire(wire); - delete wire; - wire = NULL; - } if (inputPort && outputPort) { // Check correct types assert(inputPort->type == Port::INPUT); assert(outputPort->type == Port::OUTPUT); - wire = new Wire(); - wire->outputModule = outputPort->module; - wire->outputId = outputPort->portId; - wire->inputModule = inputPort->module; - wire->inputId = inputPort->portId; - engineAddWire(wire); + if (!wire) { + wire = new Wire(); + wire->outputModule = outputPort->module; + wire->outputId = outputPort->portId; + wire->inputModule = inputPort->module; + wire->inputId = inputPort->portId; + engineAddWire(wire); + } + } + else { + if (wire) { + engineRemoveWire(wire); + delete wire; + wire = NULL; + } } } @@ -150,17 +148,17 @@ void WireWidget::draw(NVGcontext *vg) { float opacity = dynamic_cast(gScene)->toolbar->wireOpacitySlider->value / 100.0; float tension = dynamic_cast(gScene)->toolbar->wireTensionSlider->value; - // Display the actively dragged wire as opaque - if (gRackWidget->activeWire == this) + // Draw as opaque if an "incomplete" wire + if (!(inputPort && outputPort)) opacity = 1.0; drawWire(vg, getOutputPos(), getInputPos(), color, tension, opacity); - drawPlug(vg, getOutputPos(), color); - drawPlug(vg, getInputPos(), color); } void WireWidget::drawPlugs(NVGcontext *vg) { // TODO Figure out a way to draw plugs first and wires last, and cut the plug portion of the wire off. + drawPlug(vg, getOutputPos(), color); + drawPlug(vg, getInputPos(), color); }