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