@@ -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); | |||||
} | } | ||||