| @@ -83,13 +83,25 @@ struct WireWidget : OpaqueWidget { | |||||
| void drawPlugs(NVGcontext *vg); | 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 { | struct RackWidget : OpaqueWidget { | ||||
| FramebufferWidget *rails; | FramebufferWidget *rails; | ||||
| // Only put ModuleWidgets in here | // Only put ModuleWidgets in here | ||||
| Widget *moduleContainer; | Widget *moduleContainer; | ||||
| // Only put WireWidgets in here | // Only put WireWidgets in here | ||||
| Widget *wireContainer; | |||||
| WireWidget *activeWire = NULL; | |||||
| WireContainer *wireContainer; | |||||
| std::string lastPath; | std::string lastPath; | ||||
| RackWidget(); | RackWidget(); | ||||
| @@ -249,20 +261,15 @@ struct MomentarySwitch : virtual Switch { | |||||
| struct Port : OpaqueWidget { | struct Port : OpaqueWidget { | ||||
| enum PortType { | enum PortType { | ||||
| DEFAULT, | |||||
| INPUT, | INPUT, | ||||
| OUTPUT | OUTPUT | ||||
| }; | }; | ||||
| Module *module = NULL; | Module *module = NULL; | ||||
| WireWidget *connectedWire = NULL; | |||||
| PortType type = DEFAULT; | |||||
| PortType type = INPUT; | |||||
| int portId; | int portId; | ||||
| Port(); | |||||
| ~Port(); | ~Port(); | ||||
| void disconnect(); | |||||
| void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
| void onMouseDownOpaque(int button); | void onMouseDownOpaque(int button); | ||||
| void onDragEnd(); | void onDragEnd(); | ||||
| @@ -97,10 +97,10 @@ void ModuleWidget::fromJson(json_t *rootJ) { | |||||
| void ModuleWidget::disconnect() { | void ModuleWidget::disconnect() { | ||||
| for (Port *input : inputs) { | for (Port *input : inputs) { | ||||
| input->disconnect(); | |||||
| gRackWidget->wireContainer->removeAllWires(input); | |||||
| } | } | ||||
| for (Port *output : outputs) { | for (Port *output : outputs) { | ||||
| output->disconnect(); | |||||
| gRackWidget->wireContainer->removeAllWires(output); | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,109 +3,83 @@ | |||||
| namespace rack { | namespace rack { | ||||
| Port::Port() { | |||||
| box.size = Vec(20, 20); | |||||
| } | |||||
| Port::~Port() { | 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) { | 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 | // 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); | nvgGlobalAlpha(vg, 0.5); | ||||
| } | } | ||||
| } | } | ||||
| void Port::onMouseDownOpaque(int button) { | void Port::onMouseDownOpaque(int button) { | ||||
| if (button == 1) { | if (button == 1) { | ||||
| disconnect(); | |||||
| gRackWidget->wireContainer->removeTopWire(this); | |||||
| // HACK | |||||
| // Update hovered*Port of active wire if applicable | |||||
| onDragEnter(NULL); | |||||
| } | } | ||||
| } | } | ||||
| void Port::onDragEnd() { | 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() { | 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) | if (type == INPUT) | ||||
| connectedWire->inputPort = NULL; | |||||
| wire->inputPort = NULL; | |||||
| else | else | ||||
| connectedWire->outputPort = NULL; | |||||
| connectedWire->updateWire(); | |||||
| gRackWidget->activeWire = connectedWire; | |||||
| connectedWire = NULL; | |||||
| wire->outputPort = NULL; | |||||
| wire->updateWire(); | |||||
| } | } | ||||
| else { | else { | ||||
| connectedWire = new WireWidget(); | |||||
| // Create a new wire | |||||
| wire = new WireWidget(); | |||||
| if (type == INPUT) | if (type == INPUT) | ||||
| connectedWire->inputPort = this; | |||||
| wire->inputPort = this; | |||||
| else | else | ||||
| connectedWire->outputPort = this; | |||||
| gRackWidget->wireContainer->addChild(connectedWire); | |||||
| gRackWidget->activeWire = connectedWire; | |||||
| wire->outputPort = this; | |||||
| } | } | ||||
| gRackWidget->wireContainer->setActiveWire(wire); | |||||
| } | } | ||||
| void Port::onDragDrop(Widget *origin) { | 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) { | 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) | if (type == INPUT) | ||||
| gRackWidget->activeWire->hoveredInputPort = this; | |||||
| activeWire->hoveredInputPort = this; | |||||
| else | else | ||||
| gRackWidget->activeWire->hoveredOutputPort = this; | |||||
| activeWire->hoveredOutputPort = this; | |||||
| } | } | ||||
| } | } | ||||
| void Port::onDragLeave(Widget *origin) { | void Port::onDragLeave(Widget *origin) { | ||||
| if (gRackWidget->activeWire) { | |||||
| WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | |||||
| if (activeWire) { | |||||
| if (type == INPUT) | if (type == INPUT) | ||||
| gRackWidget->activeWire->hoveredInputPort = NULL; | |||||
| activeWire->hoveredInputPort = NULL; | |||||
| else | else | ||||
| gRackWidget->activeWire->hoveredOutputPort = NULL; | |||||
| activeWire->hoveredOutputPort = NULL; | |||||
| } | } | ||||
| } | } | ||||
| @@ -13,19 +13,6 @@ | |||||
| namespace rack { | 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() { | RackWidget::RackWidget() { | ||||
| rails = new FramebufferWidget(); | rails = new FramebufferWidget(); | ||||
| RackRail *rail = new RackRail(); | RackRail *rail = new RackRail(); | ||||
| @@ -45,7 +32,7 @@ RackWidget::~RackWidget() { | |||||
| } | } | ||||
| void RackWidget::clear() { | void RackWidget::clear() { | ||||
| activeWire = NULL; | |||||
| wireContainer->activeWire = NULL; | |||||
| wireContainer->clearChildren(); | wireContainer->clearChildren(); | ||||
| moduleContainer->clearChildren(); | moduleContainer->clearChildren(); | ||||
| lastPath = ""; | lastPath = ""; | ||||
| @@ -253,8 +240,6 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
| WireWidget *wireWidget = new WireWidget(); | WireWidget *wireWidget = new WireWidget(); | ||||
| wireWidget->outputPort = outputPort; | wireWidget->outputPort = outputPort; | ||||
| wireWidget->inputPort = inputPort; | wireWidget->inputPort = inputPort; | ||||
| outputPort->connectedWire = wireWidget; | |||||
| inputPort->connectedWire = wireWidget; | |||||
| wireWidget->updateWire(); | wireWidget->updateWire(); | ||||
| // Add wire to rack | // Add wire to rack | ||||
| wireContainer->addChild(wireWidget); | 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() { | WireWidget::~WireWidget() { | ||||
| if (outputPort) { | |||||
| outputPort->connectedWire = NULL; | |||||
| outputPort = NULL; | |||||
| } | |||||
| if (inputPort) { | |||||
| inputPort->connectedWire = NULL; | |||||
| inputPort = NULL; | |||||
| } | |||||
| outputPort = NULL; | |||||
| inputPort = NULL; | |||||
| updateWire(); | updateWire(); | ||||
| } | } | ||||
| void WireWidget::updateWire() { | void WireWidget::updateWire() { | ||||
| if (wire) { | |||||
| engineRemoveWire(wire); | |||||
| delete wire; | |||||
| wire = NULL; | |||||
| } | |||||
| if (inputPort && outputPort) { | if (inputPort && outputPort) { | ||||
| // Check correct types | // Check correct types | ||||
| assert(inputPort->type == Port::INPUT); | assert(inputPort->type == Port::INPUT); | ||||
| assert(outputPort->type == Port::OUTPUT); | 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 opacity = dynamic_cast<RackScene*>(gScene)->toolbar->wireOpacitySlider->value / 100.0; | ||||
| float tension = dynamic_cast<RackScene*>(gScene)->toolbar->wireTensionSlider->value; | 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; | opacity = 1.0; | ||||
| drawWire(vg, getOutputPos(), getInputPos(), color, tension, opacity); | drawWire(vg, getOutputPos(), getInputPos(), color, tension, opacity); | ||||
| drawPlug(vg, getOutputPos(), color); | |||||
| drawPlug(vg, getInputPos(), color); | |||||
| } | } | ||||
| void WireWidget::drawPlugs(NVGcontext *vg) { | 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. | // 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); | |||||
| } | } | ||||