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