Browse Source

Add history actions: ModuleAdd, ModuleRemove, ModuleMove, ParamChange, WireAdd, WireRemove, WireMove

tags/v1.0.0
Andrew Belt 6 years ago
parent
commit
c59b9ba67d
19 changed files with 359 additions and 74 deletions
  1. +1
    -0
      include/app/Knob.hpp
  2. +1
    -0
      include/app/ModuleWidget.hpp
  3. +3
    -2
      include/app/ParamWidget.hpp
  4. +2
    -1
      include/app/RackWidget.hpp
  5. +2
    -2
      include/helpers.hpp
  6. +83
    -2
      include/history.hpp
  7. +23
    -4
      src/app/Knob.cpp
  8. +10
    -2
      src/app/ModuleBrowser.cpp
  9. +25
    -14
      src/app/ModuleWidget.cpp
  10. +4
    -4
      src/app/MomentarySwitch.cpp
  11. +43
    -20
      src/app/ParamWidget.cpp
  12. +38
    -5
      src/app/RackWidget.cpp
  13. +4
    -4
      src/app/SVGKnob.cpp
  14. +2
    -2
      src/app/SVGSlider.cpp
  15. +2
    -2
      src/app/SVGSwitch.cpp
  16. +1
    -1
      src/app/Scene.cpp
  17. +19
    -4
      src/app/ToggleSwitch.cpp
  18. +24
    -5
      src/engine/Engine.cpp
  19. +72
    -0
      src/history.cpp

+ 1
- 0
include/app/Knob.hpp View File

@@ -11,6 +11,7 @@ namespace rack {
struct Knob : ParamWidget { struct Knob : ParamWidget {
/** Multiplier for mouse movement to adjust knob value */ /** Multiplier for mouse movement to adjust knob value */
float speed = 1.0; float speed = 1.0;
float oldValue = 0.f;


void onButton(const event::Button &e) override; void onButton(const event::Button &e) override;
void onDragStart(const event::DragStart &e) override; void onDragStart(const event::DragStart &e) override;


+ 1
- 0
include/app/ModuleWidget.hpp View File

@@ -22,6 +22,7 @@ struct ModuleWidget : OpaqueWidget {
std::vector<PortWidget*> outputs; std::vector<PortWidget*> outputs;
/** For RackWidget dragging */ /** For RackWidget dragging */
math::Vec dragPos; math::Vec dragPos;
math::Vec oldPos;


ModuleWidget(Module *module); ModuleWidget(Module *module);
~ModuleWidget(); ~ModuleWidget();


+ 3
- 2
include/app/ParamWidget.hpp View File

@@ -2,14 +2,15 @@
#include "app/common.hpp" #include "app/common.hpp"
#include "widgets/OpaqueWidget.hpp" #include "widgets/OpaqueWidget.hpp"
#include "ui/Tooltip.hpp" #include "ui/Tooltip.hpp"
#include "ui/Quantity.hpp"
#include "app/ParamQuantity.hpp"
#include "history.hpp"




namespace rack { namespace rack {




struct ParamWidget : OpaqueWidget { struct ParamWidget : OpaqueWidget {
Quantity *quantity = NULL;
ParamQuantity *paramQuantity = NULL;
float dirtyValue = NAN; float dirtyValue = NAN;
Tooltip *tooltip = NULL; Tooltip *tooltip = NULL;




+ 2
- 1
include/app/RackWidget.hpp View File

@@ -44,12 +44,13 @@ struct RackWidget : OpaqueWidget {
void addModule(ModuleWidget *m); void addModule(ModuleWidget *m);
void addModuleAtMouse(ModuleWidget *m); void addModuleAtMouse(ModuleWidget *m);
/** Removes the module and transfers ownership to the caller */ /** Removes the module and transfers ownership to the caller */
void deleteModule(ModuleWidget *m);
void removeModule(ModuleWidget *m);
void cloneModule(ModuleWidget *m); void cloneModule(ModuleWidget *m);
/** Sets a module's box if non-colliding. Returns true if set */ /** Sets a module's box if non-colliding. Returns true if set */
bool requestModuleBox(ModuleWidget *m, math::Rect box); bool requestModuleBox(ModuleWidget *m, math::Rect box);
/** Moves a module to the closest non-colliding position */ /** Moves a module to the closest non-colliding position */
bool requestModuleBoxNearest(ModuleWidget *m, math::Rect box); bool requestModuleBoxNearest(ModuleWidget *m, math::Rect box);
ModuleWidget *getModule(int moduleId);


void step() override; void step() override;
void draw(NVGcontext *vg) override; void draw(NVGcontext *vg) override;


+ 2
- 2
include/helpers.hpp View File

@@ -60,7 +60,7 @@ TParamWidget *createParam(math::Vec pos, Module *module, int paramId) {
ParamQuantity *q = new ParamQuantity; ParamQuantity *q = new ParamQuantity;
q->module = module; q->module = module;
q->paramId = paramId; q->paramId = paramId;
o->quantity = q;
o->paramQuantity = q;
return o; return o;
} }


@@ -71,7 +71,7 @@ TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId) {
ParamQuantity *q = new ParamQuantity; ParamQuantity *q = new ParamQuantity;
q->module = module; q->module = module;
q->paramId = paramId; q->paramId = paramId;
o->quantity = q;
o->paramQuantity = q;
return o; return o;
} }




+ 83
- 2
include/history.hpp View File

@@ -3,6 +3,7 @@
#include "math.hpp" #include "math.hpp"
#include "plugin/Model.hpp" #include "plugin/Model.hpp"
#include <vector> #include <vector>
#include <jansson.h>




namespace rack { namespace rack {
@@ -16,15 +17,95 @@ struct Action {
}; };




struct ModuleAdd : Action {
Model *model;
/** An action operating on a module
Subclass this to create your own custom actions for your module.
*/
struct ModuleAction : Action {
int moduleId; int moduleId;
};


struct ModuleAdd : ModuleAction {
Model *model;
math::Vec pos; math::Vec pos;
void undo() override; void undo() override;
void redo() override; void redo() override;
}; };




struct ModuleRemove : ModuleAction {
Model *model;
math::Vec pos;
json_t *moduleJ;

struct WireInfo {
int wireId;
int outputModuleId;
int outputId;
int inputModuleId;
int inputId;
};
std::vector<WireInfo> wireInfos;

~ModuleRemove();
void undo() override;
void redo() override;
};


struct ModuleMove : ModuleAction {
math::Vec oldPos;
math::Vec newPos;
void undo() override;
void redo() override;
};


struct ParamChange : ModuleAction {
int paramId;
float oldValue;
float newValue;
void undo() override;
void redo() override;
};


struct WireAdd : Action {
int wireId;
int outputModuleId;
int outputId;
int inputModuleId;
int inputId;
void undo() override;
void redo() override;
};


struct WireRemove : Action {
int wireId;
int outputModuleId;
int outputId;
int inputModuleId;
int inputId;
void undo() override;
void redo() override;
};


struct WireMove : Action {
int wireId;
int oldOutputModuleId;
int oldOutputId;
int oldInputModuleId;
int oldInputId;
int newOutputModuleId;
int newOutputId;
int newInputModuleId;
int newInputId;
void undo() override;
void redo() override;
};



struct State { struct State {
std::vector<Action*> actions; std::vector<Action*> actions;


+ 23
- 4
src/app/Knob.cpp View File

@@ -1,4 +1,7 @@
#include "app/Knob.hpp" #include "app/Knob.hpp"
#include "app.hpp"
#include "app/Scene.hpp"
#include "history.hpp"




namespace rack { namespace rack {
@@ -17,18 +20,34 @@ void Knob::onButton(const event::Button &e) {
} }


void Knob::onDragStart(const event::DragStart &e) { void Knob::onDragStart(const event::DragStart &e) {
if (paramQuantity)
oldValue = paramQuantity->getValue();

app()->window->cursorLock(); app()->window->cursorLock();
} }


void Knob::onDragEnd(const event::DragEnd &e) { void Knob::onDragEnd(const event::DragEnd &e) {
app()->window->cursorUnlock(); app()->window->cursorUnlock();

if (paramQuantity) {
float newValue = paramQuantity->getValue();
if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange *h = new history::ParamChange;
h->moduleId = paramQuantity->module->id;
h->paramId = paramQuantity->paramId;
h->oldValue = oldValue;
h->newValue = newValue;
app()->history->push(h);
}
}
} }


void Knob::onDragMove(const event::DragMove &e) { void Knob::onDragMove(const event::DragMove &e) {
if (quantity) {
if (paramQuantity) {
float range; float range;
if (quantity->isBounded()) {
range = quantity->getRange();
if (paramQuantity->isBounded()) {
range = paramQuantity->getRange();
} }
else { else {
// Continuous encoders scale as if their limits are +/-1 // Continuous encoders scale as if their limits are +/-1
@@ -39,7 +58,7 @@ void Knob::onDragMove(const event::DragMove &e) {
// Drag slower if Mod is held // Drag slower if Mod is held
if (app()->window->isModPressed()) if (app()->window->isModPressed())
delta /= 16.f; delta /= 16.f;
quantity->moveValue(delta);
paramQuantity->moveValue(delta);
} }


ParamWidget::onDragMove(e); ParamWidget::onDragMove(e);


+ 10
- 2
src/app/ModuleBrowser.cpp View File

@@ -8,6 +8,7 @@
#include "app/Scene.hpp" #include "app/Scene.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "app.hpp" #include "app.hpp"
#include "history.hpp"


#include <set> #include <set>
#include <algorithm> #include <algorithm>
@@ -78,12 +79,19 @@ struct ModuleBox : OpaqueWidget {
// Create module // Create module
ModuleWidget *moduleWidget = model->createModuleWidget(); ModuleWidget *moduleWidget = model->createModuleWidget();
assert(moduleWidget); assert(moduleWidget);
app()->scene->rackWidget->addModule(moduleWidget);
app()->scene->rackWidget->addModuleAtMouse(moduleWidget);
// This is a bit nonstandard/unsupported usage, but pretend the moduleWidget was clicked so it can be dragged in the RackWidget // This is a bit nonstandard/unsupported usage, but pretend the moduleWidget was clicked so it can be dragged in the RackWidget
e.consume(moduleWidget);
// e.consume(moduleWidget);
// Close Module Browser // Close Module Browser
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>();
moduleBrowser->visible = false; moduleBrowser->visible = false;

// Push ModuleAdd history action
history::ModuleAdd *h = new history::ModuleAdd;
h->model = moduleWidget->model;
h->moduleId = moduleWidget->module->id;
h->pos = moduleWidget->box.pos;
app()->history->push(h);
} }
OpaqueWidget::onButton(e); OpaqueWidget::onButton(e);
} }


+ 25
- 14
src/app/ModuleWidget.cpp View File

@@ -7,6 +7,7 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "app.hpp" #include "app.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "history.hpp"


#include "osdialog.h" #include "osdialog.h"


@@ -15,23 +16,12 @@ namespace rack {




ModuleWidget::ModuleWidget(Module *module) { ModuleWidget::ModuleWidget(Module *module) {
if (module) {
app()->engine->addModule(module);
}
this->module = module; this->module = module;
} }


ModuleWidget::~ModuleWidget() { ModuleWidget::~ModuleWidget() {
// HACK
// If we try to disconnect wires in the Module Browser (e.g. when Rack is closed while the Module Browser is open), app()->scene->rackWidget will be an invalid pointer.
// So only attempt to disconnect if the module is not NULL.
if (module)
disconnect();
// Remove and delete the Module instance
if (module) { if (module) {
app()->engine->removeModule(module);
delete module; delete module;
module = NULL;
} }
} }


@@ -327,13 +317,26 @@ void ModuleWidget::drawShadow(NVGcontext *vg) {
nvgFill(vg); nvgFill(vg);
} }


static void ModuleWidget_removeAction(ModuleWidget *moduleWidget) {
// Push ModuleRemove history action
history::ModuleRemove *h = new history::ModuleRemove;
h->model = moduleWidget->model;
h->moduleId = moduleWidget->module->id;
h->pos = moduleWidget->box.pos;
h->moduleJ = moduleWidget->toJson();
app()->history->push(h);

app()->scene->rackWidget->removeModule(moduleWidget);
delete moduleWidget;
}

void ModuleWidget::onHover(const event::Hover &e) { void ModuleWidget::onHover(const event::Hover &e) {
OpaqueWidget::onHover(e); OpaqueWidget::onHover(e);


// Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget. // Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget.
if (glfwGetKey(app()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(app()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) { if (glfwGetKey(app()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(app()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) {
if (!app()->window->isModPressed() && !app()->window->isShiftPressed()) { if (!app()->window->isModPressed() && !app()->window->isShiftPressed()) {
requestedDelete = true;
ModuleWidget_removeAction(this);
return; return;
} }
} }
@@ -401,10 +404,19 @@ void ModuleWidget::onHoverKey(const event::HoverKey &e) {
} }


void ModuleWidget::onDragStart(const event::DragStart &e) { void ModuleWidget::onDragStart(const event::DragStart &e) {
oldPos = box.pos;
dragPos = app()->scene->rackWidget->lastMousePos.minus(box.pos); dragPos = app()->scene->rackWidget->lastMousePos.minus(box.pos);
} }


void ModuleWidget::onDragEnd(const event::DragEnd &e) { void ModuleWidget::onDragEnd(const event::DragEnd &e) {
if (!box.pos.isEqual(oldPos)) {
// Push ModuleMove history action
history::ModuleMove *h = new history::ModuleMove;
h->moduleId = module->id;
h->oldPos = oldPos;
h->newPos = box.pos;
app()->history->push(h);
}
} }


void ModuleWidget::onDragMove(const event::DragMove &e) { void ModuleWidget::onDragMove(const event::DragMove &e) {
@@ -526,8 +538,7 @@ struct ModuleDeleteItem : MenuItem {
rightText = "Backspace/Delete"; rightText = "Backspace/Delete";
} }
void onAction(const event::Action &e) override { void onAction(const event::Action &e) override {
app()->scene->rackWidget->deleteModule(moduleWidget);
delete moduleWidget;
ModuleWidget_removeAction(moduleWidget);
} }
}; };




+ 4
- 4
src/app/MomentarySwitch.cpp View File

@@ -5,14 +5,14 @@ namespace rack {




void MomentarySwitch::onDragStart(const event::DragStart &e) { void MomentarySwitch::onDragStart(const event::DragStart &e) {
if (quantity) {
quantity->setMax();
if (paramQuantity) {
paramQuantity->setMax();
} }
} }


void MomentarySwitch::onDragEnd(const event::DragEnd &e) { void MomentarySwitch::onDragEnd(const event::DragEnd &e) {
if (quantity) {
quantity->setMin();
if (paramQuantity) {
paramQuantity->setMin();
} }
} }




+ 43
- 20
src/app/ParamWidget.cpp View File

@@ -6,6 +6,7 @@
#include "app.hpp" #include "app.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "random.hpp" #include "random.hpp"
#include "history.hpp"




namespace rack { namespace rack {
@@ -21,15 +22,27 @@ struct ParamField : TextField {


void setParamWidget(ParamWidget *paramWidget) { void setParamWidget(ParamWidget *paramWidget) {
this->paramWidget = paramWidget; this->paramWidget = paramWidget;
if (paramWidget->quantity)
text = paramWidget->quantity->getDisplayValueString();
if (paramWidget->paramQuantity)
text = paramWidget->paramQuantity->getDisplayValueString();
selectAll(); selectAll();
} }


void onSelectKey(const event::SelectKey &e) override { void onSelectKey(const event::SelectKey &e) override {
if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) { if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
if (paramWidget->quantity)
paramWidget->quantity->setDisplayValueString(text);
float oldValue = paramWidget->paramQuantity->getValue();
if (paramWidget->paramQuantity)
paramWidget->paramQuantity->setDisplayValueString(text);
float newValue = paramWidget->paramQuantity->getValue();

if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange *h = new history::ParamChange;
h->moduleId = paramWidget->paramQuantity->module->id;
h->paramId = paramWidget->paramQuantity->paramId;
h->oldValue = oldValue;
h->newValue = newValue;
app()->history->push(h);
}


MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); MenuOverlay *overlay = getAncestorOfType<MenuOverlay>();
overlay->requestedDelete = true; overlay->requestedDelete = true;
@@ -49,14 +62,14 @@ struct ParamField : TextField {




ParamWidget::~ParamWidget() { ParamWidget::~ParamWidget() {
if (quantity)
delete quantity;
if (paramQuantity)
delete paramQuantity;
} }


void ParamWidget::step() { void ParamWidget::step() {
if (quantity) {
float value = quantity->getValue();
// Trigger change event when quantity value changes
if (paramQuantity) {
float value = paramQuantity->getValue();
// Trigger change event when paramQuantity value changes
if (value != dirtyValue) { if (value != dirtyValue) {
dirtyValue = value; dirtyValue = value;
event::Change eChange; event::Change eChange;
@@ -65,13 +78,10 @@ void ParamWidget::step() {
} }


if (tooltip) { if (tooltip) {
// Quantity string
if (quantity) {
tooltip->text = quantity->getString();
}
// Param description
ParamQuantity *paramQuantity = dynamic_cast<ParamQuantity*>(quantity);
if (paramQuantity) { if (paramQuantity) {
// Quantity string
tooltip->text = paramQuantity->getString();
// Param description
std::string description = paramQuantity->getParam()->description; std::string description = paramQuantity->getParam()->description;
if (!description.empty()) if (!description.empty())
tooltip->text += "\n" + description; tooltip->text += "\n" + description;
@@ -86,18 +96,31 @@ void ParamWidget::step() {
void ParamWidget::fromJson(json_t *rootJ) { void ParamWidget::fromJson(json_t *rootJ) {
json_t *valueJ = json_object_get(rootJ, "value"); json_t *valueJ = json_object_get(rootJ, "value");
if (valueJ) { if (valueJ) {
if (quantity)
quantity->setValue(json_number_value(valueJ));
if (paramQuantity)
paramQuantity->setValue(json_number_value(valueJ));
} }
} }


void ParamWidget::onButton(const event::Button &e) { void ParamWidget::onButton(const event::Button &e) {
// Right click to reset // Right click to reset
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT && !(e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT && !(e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) {
if (quantity)
quantity->reset();
if (paramQuantity) {
float oldValue = paramQuantity->getValue();
paramQuantity->reset();
float newValue = paramQuantity->getValue();

if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange *h = new history::ParamChange;
h->moduleId = paramQuantity->module->id;
h->paramId = paramQuantity->paramId;
h->oldValue = oldValue;
h->newValue = newValue;
app()->history->push(h);
}
}
// Here's another way of doing it, but either works. // Here's another way of doing it, but either works.
// dynamic_cast<ParamQuantity*>(quantity)->getParam()->reset();
// paramQuantity->getParam()->reset();
e.consume(this); e.consume(this);
} }




+ 38
- 5
src/app/RackWidget.cpp View File

@@ -9,6 +9,7 @@
#include "asset.hpp" #include "asset.hpp"
#include "system.hpp" #include "system.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "engine/Engine.hpp"
#include "app.hpp" #include "app.hpp"




@@ -53,12 +54,19 @@ RackWidget::RackWidget() {
} }


RackWidget::~RackWidget() { RackWidget::~RackWidget() {
clear();
} }


void RackWidget::clear() { void RackWidget::clear() {
wireContainer->activeWire = NULL; wireContainer->activeWire = NULL;
wireContainer->clearChildren(); wireContainer->clearChildren();
moduleContainer->clearChildren();
// Remove ModuleWidgets
std::list<Widget*> widgets = moduleContainer->children;
for (Widget *w : widgets) {
ModuleWidget *moduleWidget = dynamic_cast<ModuleWidget*>(w);
assert(moduleWidget);
removeModule(moduleWidget);
}


app()->scene->scrollWidget->offset = math::Vec(0, 0); app()->scene->scrollWidget->offset = math::Vec(0, 0);
} }
@@ -455,17 +463,32 @@ void RackWidget::pastePresetClipboard() {
} }


void RackWidget::addModule(ModuleWidget *m) { void RackWidget::addModule(ModuleWidget *m) {
// Add module to ModuleContainer
assert(m);
assert(m->module);
moduleContainer->addChild(m); moduleContainer->addChild(m);

// Add module to Engine
app()->engine->addModule(m->module);
} }


void RackWidget::addModuleAtMouse(ModuleWidget *m) { void RackWidget::addModuleAtMouse(ModuleWidget *m) {
addModule(m);
assert(m);
// Move module nearest to the mouse position // Move module nearest to the mouse position
m->box.pos = lastMousePos.minus(m->box.size.div(2)); m->box.pos = lastMousePos.minus(m->box.size.div(2));
requestModuleBoxNearest(m, m->box); requestModuleBoxNearest(m, m->box);
addModule(m);
} }


void RackWidget::deleteModule(ModuleWidget *m) {
void RackWidget::removeModule(ModuleWidget *m) {
// Disconnect wires
m->disconnect();

// Remove module from Engine
assert(m->module);
app()->engine->removeModule(m->module);

// Remove module from ModuleContainer
moduleContainer->removeChild(m); moduleContainer->removeChild(m);
} }


@@ -496,8 +519,8 @@ bool RackWidget::requestModuleBox(ModuleWidget *m, math::Rect box) {


bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, math::Rect box) { bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, math::Rect box) {
// Create possible positions // Create possible positions
int x0 = roundf(box.pos.x / RACK_GRID_WIDTH);
int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT);
int x0 = std::round(box.pos.x / RACK_GRID_WIDTH);
int y0 = std::round(box.pos.y / RACK_GRID_HEIGHT);
std::vector<math::Vec> positions; std::vector<math::Vec> positions;
for (int y = std::max(0, y0 - 8); y < y0 + 8; y++) { for (int y = std::max(0, y0 - 8); y < y0 + 8; y++) {
for (int x = std::max(0, x0 - 400); x < x0 + 400; x++) { for (int x = std::max(0, x0 - 400); x < x0 + 400; x++) {
@@ -520,6 +543,16 @@ bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, math::Rect box) {
return false; return false;
} }


ModuleWidget *RackWidget::getModule(int moduleId) {
for (Widget *w : moduleContainer->children) {
ModuleWidget *moduleWidget = dynamic_cast<ModuleWidget*>(w);
assert(moduleWidget);
if (moduleWidget->module->id == moduleId)
return moduleWidget;
}
return NULL;
}

void RackWidget::step() { void RackWidget::step() {
// Expand size to fit modules // Expand size to fit modules
math::Vec moduleSize = moduleContainer->getChildrenBoundingBox().getBottomRight(); math::Vec moduleSize = moduleContainer->getChildrenBoundingBox().getBottomRight();


+ 4
- 4
src/app/SVGKnob.cpp View File

@@ -32,14 +32,14 @@ void SVGKnob::step() {


void SVGKnob::onChange(const event::Change &e) { void SVGKnob::onChange(const event::Change &e) {
// Re-transform the TransformWidget // Re-transform the TransformWidget
if (quantity) {
if (paramQuantity) {
float angle; float angle;
if (quantity->isBounded()) {
angle = math::rescale(quantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle);
if (paramQuantity->isBounded()) {
angle = math::rescale(paramQuantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle);
angle = std::fmod(angle, 2*M_PI); angle = std::fmod(angle, 2*M_PI);
} }
else { else {
angle = math::rescale(quantity->getValue(), 0.f, 1.f, minAngle, maxAngle);
angle = math::rescale(paramQuantity->getValue(), 0.f, 1.f, minAngle, maxAngle);
} }
tw->identity(); tw->identity();
// Rotate SVG // Rotate SVG


+ 2
- 2
src/app/SVGSlider.cpp View File

@@ -28,9 +28,9 @@ void SVGSlider::step() {
} }


void SVGSlider::onChange(const event::Change &e) { void SVGSlider::onChange(const event::Change &e) {
if (quantity) {
if (paramQuantity) {
// Interpolate handle position // Interpolate handle position
float v = quantity->getScaledValue();
float v = paramQuantity->getScaledValue();
handle->box.pos = math::Vec( handle->box.pos = math::Vec(
math::rescale(v, 0.f, 1.f, minHandlePos.x, maxHandlePos.x), math::rescale(v, 0.f, 1.f, minHandlePos.x, maxHandlePos.x),
math::rescale(v, 0.f, 1.f, minHandlePos.y, maxHandlePos.y)); math::rescale(v, 0.f, 1.f, minHandlePos.y, maxHandlePos.y));


+ 2
- 2
src/app/SVGSwitch.cpp View File

@@ -25,8 +25,8 @@ void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) {


void SVGSwitch::onChange(const event::Change &e) { void SVGSwitch::onChange(const event::Change &e) {
assert(frames.size() > 0); assert(frames.size() > 0);
if (quantity) {
int index = quantity->getScaledValue() * (frames.size() - 1);
if (paramQuantity) {
int index = paramQuantity->getScaledValue() * (frames.size() - 1);
index = math::clamp(index, 0, (int) frames.size() - 1); index = math::clamp(index, 0, (int) frames.size() - 1);
sw->setSVG(frames[index]); sw->setSVG(frames[index]);
dirty = true; dirty = true;


+ 1
- 1
src/app/Scene.cpp View File

@@ -76,7 +76,7 @@ void Scene::draw(NVGcontext *vg) {
} }


void Scene::onHoverKey(const event::HoverKey &e) { void Scene::onHoverKey(const event::HoverKey &e) {
if (e.action == GLFW_PRESS) {
if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
switch (e.key) { switch (e.key) {
case GLFW_KEY_N: { case GLFW_KEY_N: {
if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) {


+ 19
- 4
src/app/ToggleSwitch.cpp View File

@@ -1,4 +1,7 @@
#include "app/ToggleSwitch.hpp" #include "app/ToggleSwitch.hpp"
#include "app.hpp"
#include "app/Scene.hpp"
#include "history.hpp"




namespace rack { namespace rack {
@@ -7,12 +10,24 @@ namespace rack {
void ToggleSwitch::onDragStart(const event::DragStart &e) { void ToggleSwitch::onDragStart(const event::DragStart &e) {
// Cycle through values // Cycle through values
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3.
if (quantity) {
if (quantity->isMax()) {
quantity->setMin();
if (paramQuantity) {
float oldValue = paramQuantity->getValue();
if (paramQuantity->isMax()) {
paramQuantity->setMin();
} }
else { else {
quantity->setValue(std::floor(quantity->getValue() + 1));
paramQuantity->setValue(std::floor(paramQuantity->getValue() + 1));
}
float newValue = paramQuantity->getValue();

if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange *h = new history::ParamChange;
h->moduleId = paramQuantity->module->id;
h->paramId = paramQuantity->paramId;
h->oldValue = oldValue;
h->newValue = newValue;
app()->history->push(h);
} }
} }
} }


+ 24
- 5
src/engine/Engine.cpp View File

@@ -230,12 +230,22 @@ void Engine::addModule(Module *module) {
assert(module); assert(module);
VIPLock vipLock(internal->vipMutex); VIPLock vipLock(internal->vipMutex);
std::lock_guard<std::mutex> lock(internal->mutex); std::lock_guard<std::mutex> lock(internal->mutex);
// Set ID
assert(module->id == 0);
module->id = internal->nextModuleId++;
// Check that the module is not already added // Check that the module is not already added
auto it = std::find(modules.begin(), modules.end(), module); auto it = std::find(modules.begin(), modules.end(), module);
assert(it == modules.end()); assert(it == modules.end());
// Set ID
if (module->id == 0) {
// Automatically assign ID
module->id = internal->nextModuleId++;
}
else {
// Manual ID
// Check that the ID is not already taken
for (Module *m : modules) {
assert(module->id != m->id);
}
}
// Add module
modules.push_back(module); modules.push_back(module);
} }


@@ -299,8 +309,17 @@ void Engine::addWire(Wire *wire) {
assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId)); assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId));
} }
// Set ID // Set ID
assert(wire->id == 0);
wire->id = internal->nextWireId++;
if (wire->id == 0) {
// Automatically assign ID
wire->id = internal->nextWireId++;
}
else {
// Manual ID
// Check that the ID is not already taken
for (Wire *w : wires) {
assert(wire->id != w->id);
}
}
// Add the wire // Add the wire
wires.push_back(wire); wires.push_back(wire);
Engine_updateActive(this); Engine_updateActive(this);


+ 72
- 0
src/history.cpp View File

@@ -1,10 +1,82 @@
#include "history.hpp" #include "history.hpp"
#include "app.hpp"
#include "app/Scene.hpp"




namespace rack { namespace rack {
namespace history { namespace history {




void ModuleAdd::undo() {
ModuleWidget *moduleWidget = app()->scene->rackWidget->getModule(moduleId);
assert(moduleWidget);
app()->scene->rackWidget->removeModule(moduleWidget);
delete moduleWidget;
}

void ModuleAdd::redo() {
ModuleWidget *moduleWidget = model->createModuleWidget();
assert(moduleWidget);
assert(moduleWidget->module);
moduleWidget->module->id = moduleId;
moduleWidget->box.pos = pos;
app()->scene->rackWidget->addModule(moduleWidget);
}


ModuleRemove::~ModuleRemove() {
json_decref(moduleJ);
}

void ModuleRemove::undo() {
ModuleWidget *moduleWidget = model->createModuleWidget();
assert(moduleWidget);
assert(moduleWidget->module);
moduleWidget->module->id = moduleId;
moduleWidget->box.pos = pos;
moduleWidget->fromJson(moduleJ);
app()->scene->rackWidget->addModule(moduleWidget);

// Add wires
for (WireInfo &wireInfo : wireInfos) {
// TODO Add wire
}
}

void ModuleRemove::redo() {
ModuleWidget *moduleWidget = app()->scene->rackWidget->getModule(moduleId);
assert(moduleWidget);
app()->scene->rackWidget->removeModule(moduleWidget);
delete moduleWidget;
}


void ModuleMove::undo() {
ModuleWidget *moduleWidget = app()->scene->rackWidget->getModule(moduleId);
assert(moduleWidget);
moduleWidget->box.pos = oldPos;
}

void ModuleMove::redo() {
ModuleWidget *moduleWidget = app()->scene->rackWidget->getModule(moduleId);
assert(moduleWidget);
moduleWidget->box.pos = newPos;
}


void ParamChange::undo() {
ModuleWidget *moduleWidget = app()->scene->rackWidget->getModule(moduleId);
assert(moduleWidget);
moduleWidget->module->params[paramId].value = oldValue;
}

void ParamChange::redo() {
ModuleWidget *moduleWidget = app()->scene->rackWidget->getModule(moduleId);
assert(moduleWidget);
moduleWidget->module->params[paramId].value = newValue;
}


State::~State() { State::~State() {
for (Action *action : actions) { for (Action *action : actions) {
delete action; delete action;


Loading…
Cancel
Save