| @@ -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(); | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -13,19 +13,6 @@ | |||
| namespace rack { | |||
| struct WireContainer : TransparentWidget { | |||
| void draw(NVGcontext *vg) { | |||
| // Wire plugs | |||
| for (Widget *child : children) { | |||
| WireWidget *wire = dynamic_cast<WireWidget*>(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); | |||
| @@ -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<WireWidget*> wires; | |||
| for (Widget *child : children) { | |||
| WireWidget *wire = dynamic_cast<WireWidget*>(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<WireWidget*>(*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<WireWidget*>(child); | |||
| assert(wire); | |||
| wire->drawPlugs(vg); | |||
| } | |||
| } | |||
| } // namespace rack | |||
| @@ -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<RackScene*>(gScene)->toolbar->wireOpacitySlider->value / 100.0; | |||
| float tension = dynamic_cast<RackScene*>(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); | |||
| } | |||