@@ -24,8 +24,10 @@ struct CableContainer : TransparentWidget { | |||||
/** Takes ownership of `w` and adds it as a child if it isn't already */ | /** Takes ownership of `w` and adds it as a child if it isn't already */ | ||||
void setIncompleteCable(CableWidget *w); | void setIncompleteCable(CableWidget *w); | ||||
CableWidget *releaseIncompleteCable(); | 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 *getTopCable(PortWidget *port); | ||||
CableWidget *getCable(int cableId); | |||||
json_t *toJson(); | json_t *toJson(); | ||||
void fromJson(json_t *rootJ, const std::map<int, ModuleWidget*> &moduleWidgets); | void fromJson(json_t *rootJ, const std::map<int, ModuleWidget*> &moduleWidgets); | ||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
@@ -21,8 +21,8 @@ struct CableWidget : OpaqueWidget { | |||||
CableWidget(); | CableWidget(); | ||||
~CableWidget(); | ~CableWidget(); | ||||
bool isComplete(); | bool isComplete(); | ||||
void setOutputPort(PortWidget *outputPort); | |||||
void setInputPort(PortWidget *inputPort); | |||||
void setOutput(PortWidget *outputPort); | |||||
void setInput(PortWidget *inputPort); | |||||
math::Vec getOutputPos(); | math::Vec getOutputPos(); | ||||
math::Vec getInputPos(); | math::Vec getInputPos(); | ||||
json_t *toJson(); | json_t *toJson(); | ||||
@@ -18,8 +18,8 @@ struct ModuleWidget : OpaqueWidget { | |||||
Widget *panel = NULL; | Widget *panel = NULL; | ||||
std::vector<ParamWidget*> params; | std::vector<ParamWidget*> params; | ||||
std::vector<PortWidget*> inputs; | |||||
std::vector<PortWidget*> outputs; | std::vector<PortWidget*> outputs; | ||||
std::vector<PortWidget*> inputs; | |||||
/** For RackWidget dragging */ | /** For RackWidget dragging */ | ||||
math::Vec dragPos; | math::Vec dragPos; | ||||
math::Vec oldPos; | math::Vec oldPos; | ||||
@@ -44,12 +44,15 @@ struct ModuleWidget : OpaqueWidget { | |||||
Transfers ownership | Transfers ownership | ||||
*/ | */ | ||||
void setModule(Module *module); | void setModule(Module *module); | ||||
void setPanel(std::shared_ptr<SVG> svg); | |||||
/** Convenience functions for adding special widgets (calls addChild()) */ | /** Convenience functions for adding special widgets (calls addChild()) */ | ||||
void addInput(PortWidget *input); | |||||
void addOutput(PortWidget *output); | |||||
void addParam(ParamWidget *param); | 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. | /** Overriding these is deprecated. | ||||
Use Module::dataToJson() and dataFromJson() instead | Use Module::dataToJson() and dataFromJson() instead | ||||
@@ -15,8 +15,8 @@ namespace rack { | |||||
struct Module { | struct Module { | ||||
int id = 0; | int id = 0; | ||||
std::vector<Param> params; | std::vector<Param> params; | ||||
std::vector<Input> inputs; | |||||
std::vector<Output> outputs; | std::vector<Output> outputs; | ||||
std::vector<Input> inputs; | |||||
std::vector<Light> lights; | std::vector<Light> lights; | ||||
/** For power meter */ | /** For power meter */ | ||||
float cpuTime = 0.f; | float cpuTime = 0.f; | ||||
@@ -1,12 +1,18 @@ | |||||
#pragma once | #pragma once | ||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "math.hpp" | #include "math.hpp" | ||||
#include "color.hpp" | |||||
#include "plugin/Model.hpp" | #include "plugin/Model.hpp" | ||||
#include <vector> | #include <vector> | ||||
#include <jansson.h> | #include <jansson.h> | ||||
namespace rack { | namespace rack { | ||||
struct CableWidget; | |||||
namespace history { | 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 */ | /** Batches multiple actions into one */ | ||||
struct ComplexAction : Action { | struct ComplexAction : Action { | ||||
/** Ordered by time occurred. Undoing will replay them backwards. */ | /** Ordered by time occurred. Undoing will replay them backwards. */ | ||||
@@ -84,20 +101,14 @@ struct CableAdd : Action { | |||||
int outputId; | int outputId; | ||||
int inputModuleId; | int inputModuleId; | ||||
int inputId; | int inputId; | ||||
NVGcolor color; | |||||
void setCable(CableWidget *cw); | |||||
void undo() override; | void undo() override; | ||||
void redo() 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 { | struct State { | ||||
@@ -86,6 +86,16 @@ CableWidget *CableContainer::getTopCable(PortWidget *port) { | |||||
return NULL; | 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 *CableContainer::toJson() { | ||||
json_t *rootJ = json_array(); | json_t *rootJ = json_array(); | ||||
for (Widget *w : children) { | for (Widget *w : children) { | ||||
@@ -99,7 +99,7 @@ bool CableWidget::isComplete() { | |||||
return outputPort && inputPort; | return outputPort && inputPort; | ||||
} | } | ||||
void CableWidget::setOutputPort(PortWidget *outputPort) { | |||||
void CableWidget::setOutput(PortWidget *outputPort) { | |||||
this->outputPort = outputPort; | this->outputPort = outputPort; | ||||
if (outputPort) { | if (outputPort) { | ||||
assert(outputPort->type == PortWidget::OUTPUT); | 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; | this->inputPort = inputPort; | ||||
if (inputPort) { | if (inputPort) { | ||||
assert(inputPort->type == PortWidget::INPUT); | assert(inputPort->type == PortWidget::INPUT); | ||||
@@ -145,7 +145,7 @@ json_t *CableWidget::toJson() { | |||||
assert(isComplete()); | assert(isComplete()); | ||||
json_t *rootJ = json_object(); | 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, "id", json_integer(cable->id)); | ||||
json_object_set_new(rootJ, "outputModuleId", json_integer(cable->outputModule->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 | // Set ports | ||||
if (app()->patch->isLegacy(1)) { | 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. | // 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 { | else { | ||||
for (PortWidget *port : outputModule->outputs) { | for (PortWidget *port : outputModule->outputs) { | ||||
if (port->portId == outputId) { | if (port->portId == outputId) { | ||||
setOutputPort(port); | |||||
setOutput(port); | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
for (PortWidget *port : inputModule->inputs) { | for (PortWidget *port : inputModule->inputs) { | ||||
if (port->portId == inputId) { | if (port->portId == inputId) { | ||||
setInputPort(port); | |||||
setInput(port); | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -162,6 +162,16 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||||
nvgFill(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); | // nvgResetScissor(vg); | ||||
} | } | ||||
@@ -282,23 +292,6 @@ void ModuleWidget::setModule(Module *module) { | |||||
this->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) { | void ModuleWidget::setPanel(std::shared_ptr<SVG> svg) { | ||||
// Remove old panel | // Remove old panel | ||||
if (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 *ModuleWidget::toJson() { | ||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
@@ -2,6 +2,7 @@ | |||||
#include "app/Scene.hpp" | #include "app/Scene.hpp" | ||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "history.hpp" | |||||
#include "componentlibrary.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) { | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | ||||
CableWidget *cw = app()->scene->rackWidget->cableContainer->getTopCable(this); | CableWidget *cw = app()->scene->rackWidget->cableContainer->getTopCable(this); | ||||
if (cw) { | if (cw) { | ||||
// history::CableRemove | |||||
history::CableRemove *h = new history::CableRemove; | |||||
h->setCable(cw); | |||||
app()->history->push(h); | |||||
app()->scene->rackWidget->cableContainer->removeCable(cw); | app()->scene->rackWidget->cableContainer->removeCable(cw); | ||||
delete cw; | delete cw; | ||||
} | } | ||||
@@ -78,20 +84,25 @@ void PortWidget::onDragStart(const event::DragStart &e) { | |||||
} | } | ||||
if (cw) { | if (cw) { | ||||
// history::CableRemove | |||||
history::CableRemove *h = new history::CableRemove; | |||||
h->setCable(cw); | |||||
app()->history->push(h); | |||||
// Disconnect and reuse existing cable | // Disconnect and reuse existing cable | ||||
app()->scene->rackWidget->cableContainer->removeCable(cw); | app()->scene->rackWidget->cableContainer->removeCable(cw); | ||||
if (type == OUTPUT) | if (type == OUTPUT) | ||||
cw->setOutputPort(NULL); | |||||
cw->setOutput(NULL); | |||||
else | else | ||||
cw->setInputPort(NULL); | |||||
cw->setInput(NULL); | |||||
} | } | ||||
else { | else { | ||||
// Create a new cable | // Create a new cable | ||||
cw = new CableWidget; | cw = new CableWidget; | ||||
if (type == OUTPUT) | if (type == OUTPUT) | ||||
cw->setOutputPort(this); | |||||
cw->setOutput(this); | |||||
else | else | ||||
cw->setInputPort(this); | |||||
cw->setInput(this); | |||||
} | } | ||||
app()->scene->rackWidget->cableContainer->setIncompleteCable(cw); | app()->scene->rackWidget->cableContainer->setIncompleteCable(cw); | ||||
} | } | ||||
@@ -102,6 +113,11 @@ void PortWidget::onDragEnd(const event::DragEnd &e) { | |||||
CableWidget *cw = app()->scene->rackWidget->cableContainer->releaseIncompleteCable(); | CableWidget *cw = app()->scene->rackWidget->cableContainer->releaseIncompleteCable(); | ||||
if (cw->isComplete()) { | if (cw->isComplete()) { | ||||
app()->scene->rackWidget->cableContainer->addCable(cw); | app()->scene->rackWidget->cableContainer->addCable(cw); | ||||
// history::CableAdd | |||||
history::CableAdd *h = new history::CableAdd; | |||||
h->setCable(cw); | |||||
app()->history->push(h); | |||||
} | } | ||||
else { | else { | ||||
delete cw; | delete cw; | ||||
@@ -119,9 +135,9 @@ void PortWidget::onDragDrop(const event::DragDrop &e) { | |||||
if (cw) { | if (cw) { | ||||
cw->hoveredOutputPort = cw->hoveredInputPort = NULL; | cw->hoveredOutputPort = cw->hoveredInputPort = NULL; | ||||
if (type == OUTPUT) | if (type == OUTPUT) | ||||
cw->setOutputPort(this); | |||||
cw->setOutput(this); | |||||
else | else | ||||
cw->setInputPort(this); | |||||
cw->setInput(this); | |||||
} | } | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
#include "history.hpp" | #include "history.hpp" | ||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "app/Scene.hpp" | #include "app/Scene.hpp" | ||||
#include "engine/Cable.hpp" | |||||
namespace rack { | 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() { | State::~State() { | ||||
for (Action *action : actions) { | for (Action *action : actions) { | ||||
delete action; | delete action; | ||||