@@ -24,8 +24,10 @@ struct CableContainer : TransparentWidget { | |||
/** Takes ownership of `w` and adds it as a child if it isn't already */ | |||
void setIncompleteCable(CableWidget *w); | |||
CableWidget *releaseIncompleteCable(); | |||
/** Returns the most recently added cable connected to the given Port, i.e. the top of the stack */ | |||
/** Returns the most recently added complete cable connected to the given Port, i.e. the top of the stack */ | |||
CableWidget *getTopCable(PortWidget *port); | |||
CableWidget *getCable(int cableId); | |||
json_t *toJson(); | |||
void fromJson(json_t *rootJ, const std::map<int, ModuleWidget*> &moduleWidgets); | |||
void draw(NVGcontext *vg) override; | |||
@@ -21,8 +21,8 @@ struct CableWidget : OpaqueWidget { | |||
CableWidget(); | |||
~CableWidget(); | |||
bool isComplete(); | |||
void setOutputPort(PortWidget *outputPort); | |||
void setInputPort(PortWidget *inputPort); | |||
void setOutput(PortWidget *outputPort); | |||
void setInput(PortWidget *inputPort); | |||
math::Vec getOutputPos(); | |||
math::Vec getInputPos(); | |||
json_t *toJson(); | |||
@@ -18,8 +18,8 @@ struct ModuleWidget : OpaqueWidget { | |||
Widget *panel = NULL; | |||
std::vector<ParamWidget*> params; | |||
std::vector<PortWidget*> inputs; | |||
std::vector<PortWidget*> outputs; | |||
std::vector<PortWidget*> inputs; | |||
/** For RackWidget dragging */ | |||
math::Vec dragPos; | |||
math::Vec oldPos; | |||
@@ -44,12 +44,15 @@ struct ModuleWidget : OpaqueWidget { | |||
Transfers ownership | |||
*/ | |||
void setModule(Module *module); | |||
void setPanel(std::shared_ptr<SVG> svg); | |||
/** Convenience functions for adding special widgets (calls addChild()) */ | |||
void addInput(PortWidget *input); | |||
void addOutput(PortWidget *output); | |||
void addParam(ParamWidget *param); | |||
void setPanel(std::shared_ptr<SVG> svg); | |||
void addOutput(PortWidget *output); | |||
void addInput(PortWidget *input); | |||
ParamWidget *getParam(int paramId); | |||
PortWidget *getOutput(int outputId); | |||
PortWidget *getInput(int inputId); | |||
/** Overriding these is deprecated. | |||
Use Module::dataToJson() and dataFromJson() instead | |||
@@ -15,8 +15,8 @@ namespace rack { | |||
struct Module { | |||
int id = 0; | |||
std::vector<Param> params; | |||
std::vector<Input> inputs; | |||
std::vector<Output> outputs; | |||
std::vector<Input> inputs; | |||
std::vector<Light> lights; | |||
/** For power meter */ | |||
float cpuTime = 0.f; | |||
@@ -1,12 +1,18 @@ | |||
#pragma once | |||
#include "common.hpp" | |||
#include "math.hpp" | |||
#include "color.hpp" | |||
#include "plugin/Model.hpp" | |||
#include <vector> | |||
#include <jansson.h> | |||
namespace rack { | |||
struct CableWidget; | |||
namespace history { | |||
@@ -17,6 +23,17 @@ struct Action { | |||
}; | |||
template <class TAction> | |||
struct InverseAction : TAction { | |||
void undo() override { | |||
TAction::redo(); | |||
} | |||
void redo() override { | |||
TAction::undo(); | |||
} | |||
}; | |||
/** Batches multiple actions into one */ | |||
struct ComplexAction : Action { | |||
/** Ordered by time occurred. Undoing will replay them backwards. */ | |||
@@ -84,20 +101,14 @@ struct CableAdd : Action { | |||
int outputId; | |||
int inputModuleId; | |||
int inputId; | |||
NVGcolor color; | |||
void setCable(CableWidget *cw); | |||
void undo() override; | |||
void redo() override; | |||
}; | |||
struct CableRemove : Action { | |||
int cableId; | |||
int outputModuleId; | |||
int outputId; | |||
int inputModuleId; | |||
int inputId; | |||
void undo() override; | |||
void redo() override; | |||
}; | |||
struct CableRemove : InverseAction<CableAdd> {}; | |||
struct State { | |||
@@ -86,6 +86,16 @@ CableWidget *CableContainer::getTopCable(PortWidget *port) { | |||
return NULL; | |||
} | |||
CableWidget *CableContainer::getCable(int cableId) { | |||
for (Widget *w : children) { | |||
CableWidget *cw = dynamic_cast<CableWidget*>(w); | |||
assert(cw); | |||
if (cw->cable->id == cableId) | |||
return cw; | |||
} | |||
return NULL; | |||
} | |||
json_t *CableContainer::toJson() { | |||
json_t *rootJ = json_array(); | |||
for (Widget *w : children) { | |||
@@ -99,7 +99,7 @@ bool CableWidget::isComplete() { | |||
return outputPort && inputPort; | |||
} | |||
void CableWidget::setOutputPort(PortWidget *outputPort) { | |||
void CableWidget::setOutput(PortWidget *outputPort) { | |||
this->outputPort = outputPort; | |||
if (outputPort) { | |||
assert(outputPort->type == PortWidget::OUTPUT); | |||
@@ -108,7 +108,7 @@ void CableWidget::setOutputPort(PortWidget *outputPort) { | |||
} | |||
} | |||
void CableWidget::setInputPort(PortWidget *inputPort) { | |||
void CableWidget::setInput(PortWidget *inputPort) { | |||
this->inputPort = inputPort; | |||
if (inputPort) { | |||
assert(inputPort->type == PortWidget::INPUT); | |||
@@ -145,7 +145,7 @@ json_t *CableWidget::toJson() { | |||
assert(isComplete()); | |||
json_t *rootJ = json_object(); | |||
// This is just here for fun. It is not used in fromJson() | |||
// This is just here for fun. It is not used in fromJson(), since cableIds are not preserved across multiple launches of Rack. | |||
json_object_set_new(rootJ, "id", json_integer(cable->id)); | |||
json_object_set_new(rootJ, "outputModuleId", json_integer(cable->outputModule->id)); | |||
@@ -177,19 +177,19 @@ void CableWidget::fromJson(json_t *rootJ, const std::map<int, ModuleWidget*> &mo | |||
// Set ports | |||
if (app()->patch->isLegacy(1)) { | |||
// Before 0.6, the index of the "ports" array was the index of the PortWidget in the `outputs` and `inputs` vector. | |||
setOutputPort(outputModule->outputs[outputId]); | |||
setInputPort(inputModule->inputs[inputId]); | |||
setOutput(outputModule->outputs[outputId]); | |||
setInput(inputModule->inputs[inputId]); | |||
} | |||
else { | |||
for (PortWidget *port : outputModule->outputs) { | |||
if (port->portId == outputId) { | |||
setOutputPort(port); | |||
setOutput(port); | |||
break; | |||
} | |||
} | |||
for (PortWidget *port : inputModule->inputs) { | |||
if (port->portId == inputId) { | |||
setInputPort(port); | |||
setInput(port); | |||
break; | |||
} | |||
} | |||
@@ -162,6 +162,16 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
nvgFill(vg); | |||
} | |||
// if (module) { | |||
// nvgBeginPath(vg); | |||
// nvgRect(vg, 0, 0, 20, 20); | |||
// nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.75)); | |||
// nvgFill(vg); | |||
// std::string debugText = string::f("%d", module->id); | |||
// bndLabel(vg, 0, 0, INFINITY, INFINITY, -1, debugText.c_str()); | |||
// } | |||
// nvgResetScissor(vg); | |||
} | |||
@@ -282,23 +292,6 @@ void ModuleWidget::setModule(Module *module) { | |||
this->module = module; | |||
} | |||
void ModuleWidget::addInput(PortWidget *input) { | |||
assert(input->type == PortWidget::INPUT); | |||
inputs.push_back(input); | |||
addChild(input); | |||
} | |||
void ModuleWidget::addOutput(PortWidget *output) { | |||
assert(output->type == PortWidget::OUTPUT); | |||
outputs.push_back(output); | |||
addChild(output); | |||
} | |||
void ModuleWidget::addParam(ParamWidget *param) { | |||
params.push_back(param); | |||
addChild(param); | |||
} | |||
void ModuleWidget::setPanel(std::shared_ptr<SVG> svg) { | |||
// Remove old panel | |||
if (panel) { | |||
@@ -316,6 +309,40 @@ void ModuleWidget::setPanel(std::shared_ptr<SVG> svg) { | |||
} | |||
} | |||
void ModuleWidget::addParam(ParamWidget *param) { | |||
params.push_back(param); | |||
addChild(param); | |||
} | |||
void ModuleWidget::addOutput(PortWidget *output) { | |||
assert(output->type == PortWidget::OUTPUT); | |||
outputs.push_back(output); | |||
addChild(output); | |||
} | |||
void ModuleWidget::addInput(PortWidget *input) { | |||
assert(input->type == PortWidget::INPUT); | |||
inputs.push_back(input); | |||
addChild(input); | |||
} | |||
ParamWidget *ModuleWidget::getParam(int paramId) { | |||
if (0 <= paramId && paramId < (int) params.size()) | |||
return params[paramId]; | |||
return NULL; | |||
} | |||
PortWidget *ModuleWidget::getOutput(int outputId) { | |||
if (0 <= outputId && outputId < (int) outputs.size()) | |||
return outputs[outputId]; | |||
return NULL; | |||
} | |||
PortWidget *ModuleWidget::getInput(int inputId) { | |||
if (0 <= inputId && inputId < (int) inputs.size()) | |||
return inputs[inputId]; | |||
return NULL; | |||
} | |||
json_t *ModuleWidget::toJson() { | |||
json_t *rootJ = json_object(); | |||
@@ -2,6 +2,7 @@ | |||
#include "app/Scene.hpp" | |||
#include "window.hpp" | |||
#include "app.hpp" | |||
#include "history.hpp" | |||
#include "componentlibrary.hpp" | |||
@@ -60,6 +61,11 @@ void PortWidget::onButton(const event::Button &e) { | |||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | |||
CableWidget *cw = app()->scene->rackWidget->cableContainer->getTopCable(this); | |||
if (cw) { | |||
// history::CableRemove | |||
history::CableRemove *h = new history::CableRemove; | |||
h->setCable(cw); | |||
app()->history->push(h); | |||
app()->scene->rackWidget->cableContainer->removeCable(cw); | |||
delete cw; | |||
} | |||
@@ -78,20 +84,25 @@ void PortWidget::onDragStart(const event::DragStart &e) { | |||
} | |||
if (cw) { | |||
// history::CableRemove | |||
history::CableRemove *h = new history::CableRemove; | |||
h->setCable(cw); | |||
app()->history->push(h); | |||
// Disconnect and reuse existing cable | |||
app()->scene->rackWidget->cableContainer->removeCable(cw); | |||
if (type == OUTPUT) | |||
cw->setOutputPort(NULL); | |||
cw->setOutput(NULL); | |||
else | |||
cw->setInputPort(NULL); | |||
cw->setInput(NULL); | |||
} | |||
else { | |||
// Create a new cable | |||
cw = new CableWidget; | |||
if (type == OUTPUT) | |||
cw->setOutputPort(this); | |||
cw->setOutput(this); | |||
else | |||
cw->setInputPort(this); | |||
cw->setInput(this); | |||
} | |||
app()->scene->rackWidget->cableContainer->setIncompleteCable(cw); | |||
} | |||
@@ -102,6 +113,11 @@ void PortWidget::onDragEnd(const event::DragEnd &e) { | |||
CableWidget *cw = app()->scene->rackWidget->cableContainer->releaseIncompleteCable(); | |||
if (cw->isComplete()) { | |||
app()->scene->rackWidget->cableContainer->addCable(cw); | |||
// history::CableAdd | |||
history::CableAdd *h = new history::CableAdd; | |||
h->setCable(cw); | |||
app()->history->push(h); | |||
} | |||
else { | |||
delete cw; | |||
@@ -119,9 +135,9 @@ void PortWidget::onDragDrop(const event::DragDrop &e) { | |||
if (cw) { | |||
cw->hoveredOutputPort = cw->hoveredInputPort = NULL; | |||
if (type == OUTPUT) | |||
cw->setOutputPort(this); | |||
cw->setOutput(this); | |||
else | |||
cw->setInputPort(this); | |||
cw->setInput(this); | |||
} | |||
} | |||
@@ -1,6 +1,7 @@ | |||
#include "history.hpp" | |||
#include "app.hpp" | |||
#include "app/Scene.hpp" | |||
#include "engine/Cable.hpp" | |||
namespace rack { | |||
@@ -109,6 +110,47 @@ void ParamChange::redo() { | |||
} | |||
void CableAdd::setCable(CableWidget *cw) { | |||
assert(cw->cable); | |||
assert(cw->cable->id > 0); | |||
cableId = cw->cable->id; | |||
assert(cw->cable->outputModule); | |||
outputModuleId = cw->cable->outputModule->id; | |||
outputId = cw->cable->outputId; | |||
assert(cw->cable->inputModule); | |||
inputModuleId = cw->cable->inputModule->id; | |||
inputId = cw->cable->inputId; | |||
color = cw->color; | |||
} | |||
void CableAdd::undo() { | |||
CableWidget *cw = app()->scene->rackWidget->cableContainer->getCable(cableId); | |||
app()->scene->rackWidget->cableContainer->removeCable(cw); | |||
delete cw; | |||
} | |||
void CableAdd::redo() { | |||
CableWidget *cw = new CableWidget; | |||
cw->cable->id = cableId; | |||
ModuleWidget *outputModule = app()->scene->rackWidget->getModule(outputModuleId); | |||
assert(outputModule); | |||
PortWidget *outputPort = outputModule->getOutput(outputId); | |||
assert(outputPort); | |||
cw->setOutput(outputPort); | |||
ModuleWidget *inputModule = app()->scene->rackWidget->getModule(inputModuleId); | |||
assert(inputModule); | |||
PortWidget *inputPort = inputModule->getInput(inputId); | |||
assert(inputPort); | |||
cw->setInput(inputPort); | |||
cw->color = color; | |||
app()->scene->rackWidget->cableContainer->addCable(cw); | |||
} | |||
State::~State() { | |||
for (Action *action : actions) { | |||
delete action; | |||