Browse Source

Clean up ModuleWidget

tags/v1.0.0
Andrew Belt 6 years ago
parent
commit
c999df235d
13 changed files with 165 additions and 116 deletions
  1. +3
    -10
      include/app/ModuleWidget.hpp
  2. +0
    -2
      include/app/MomentarySwitch.hpp
  3. +0
    -2
      include/app/ParamWidget.hpp
  4. +1
    -0
      include/app/RackWidget.hpp
  5. +5
    -3
      include/engine/Module.hpp
  6. +2
    -0
      include/engine/Param.hpp
  7. +108
    -71
      src/app/ModuleWidget.cpp
  8. +7
    -21
      src/app/ParamWidget.cpp
  9. +8
    -4
      src/app/RackWidget.cpp
  10. +1
    -1
      src/app/Toolbar.cpp
  11. +2
    -2
      src/engine/Engine.cpp
  12. +14
    -0
      src/engine/Module.cpp
  13. +14
    -0
      src/engine/Param.cpp

+ 3
- 10
include/app/ModuleWidget.hpp View File

@@ -2,7 +2,6 @@
#include "app/common.hpp"
#include "widgets/OpaqueWidget.hpp"
#include "ui/Menu.hpp"
#include "app/SVGPanel.hpp"
#include "app/Port.hpp"
#include "app/ParamWidget.hpp"
#include "plugin/Model.hpp"
@@ -12,19 +11,15 @@
namespace rack {


struct SVGPanel;
struct Port;


struct ModuleWidget : OpaqueWidget {
Model *model = NULL;
/** Owns the module pointer */
Module *module = NULL;

SVGPanel *panel = NULL;
Widget *panel = NULL;
std::vector<ParamWidget*> params;
std::vector<Port*> inputs;
std::vector<Port*> outputs;
std::vector<ParamWidget*> params;

ModuleWidget(Module *module);
~ModuleWidget();
@@ -39,13 +34,11 @@ struct ModuleWidget : OpaqueWidget {

void copyClipboard();
void pasteClipboard();
void save(std::string filename);
void load(std::string filename);
void save(std::string filename);
void loadDialog();
void saveDialog();

virtual void create();
virtual void _delete();
/** Disconnects cables from all ports
Called when the user clicks Disconnect Cables in the context menu.
*/


+ 0
- 2
include/app/MomentarySwitch.hpp View File

@@ -10,8 +10,6 @@ namespace rack {
Consider using SVGButton if the switch simply changes the state of your Module when clicked.
*/
struct MomentarySwitch : virtual ParamWidget {
/** Don't randomize state */
void randomize() override {}
void onDragStart(event::DragStart &e) override;
void onDragEnd(event::DragEnd &e) override;
};


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

@@ -17,8 +17,6 @@ struct ParamWidget : OpaqueWidget {

/** For legacy patch loading */
void fromJson(json_t *rootJ);
virtual void reset();
virtual void randomize();
void onButton(event::Button &e) override;
};



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

@@ -53,6 +53,7 @@ struct RackWidget : OpaqueWidget {
void draw(NVGcontext *vg) override;

void onHover(event::Hover &e) override;
void onDragHover(event::DragHover &e) override;
void onButton(event::Button &e) override;
void onZoom(event::Zoom &e) override;
};


+ 5
- 3
include/engine/Module.hpp View File

@@ -39,6 +39,11 @@ struct Module {
lights.resize(numLights);
}

json_t *toJson();
void fromJson(json_t *rootJ);
void reset();
void randomize();

/** Advances the module by 1 audio frame with duration 1.0 / gSampleRate
Override this method to read inputs and params, and to write outputs and lights.
*/
@@ -51,9 +56,6 @@ struct Module {
/** Called when user clicks Randomize in the module context menu */
virtual void onRandomize() {}

json_t *toJson();
void fromJson(json_t *rootJ);

/** Override these to store extra internal data in the "data" property of the module's JSON object */
virtual json_t *dataToJson() { return NULL; }
virtual void dataFromJson(json_t *root) {}


+ 2
- 0
include/engine/Param.hpp View File

@@ -34,6 +34,8 @@ struct Param {

json_t *toJson();
void fromJson(json_t *rootJ);
void reset();
void randomize();
};




+ 108
- 71
src/app/ModuleWidget.cpp View File

@@ -3,6 +3,7 @@
#include "system.hpp"
#include "asset.hpp"
#include "app/Scene.hpp"
#include "app/SVGPanel.hpp"
#include "helpers.hpp"
#include "context.hpp"
#include "settings.hpp"
@@ -56,11 +57,13 @@ void ModuleWidget::setPanel(std::shared_ptr<SVG> svg) {
panel = NULL;
}

panel = new SVGPanel;
panel->setBackground(svg);
addChild(panel);

box.size = panel->box.size;
{
SVGPanel *panel = new SVGPanel;
panel->setBackground(svg);
addChild(panel);
box.size = panel->box.size;
this->panel = panel;
}
}


@@ -123,10 +126,14 @@ void ModuleWidget::fromJson(json_t *rootJ) {

void ModuleWidget::copyClipboard() {
json_t *moduleJ = toJson();
DEFER({
json_decref(moduleJ);
});
char *moduleJson = json_dumps(moduleJ, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
DEFER({
free(moduleJson);
});
glfwSetClipboardString(context()->window->win, moduleJson);
free(moduleJson);
json_decref(moduleJ);
}

void ModuleWidget::pasteClipboard() {
@@ -138,50 +145,61 @@ void ModuleWidget::pasteClipboard() {

json_error_t error;
json_t *moduleJ = json_loads(moduleJson, 0, &error);
if (moduleJ) {
fromJson(moduleJ);
json_decref(moduleJ);
}
else {
if (!moduleJ) {
WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
return;
}
DEFER({
json_decref(moduleJ);
});

fromJson(moduleJ);
}

void ModuleWidget::load(std::string filename) {
INFO("Loading preset %s", filename.c_str());

FILE *file = fopen(filename.c_str(), "r");
if (!file) {
// Exit silently
WARN("Could not load patch file %s", filename.c_str());
return;
}
DEFER({
fclose(file);
});

json_error_t error;
json_t *moduleJ = json_loadf(file, 0, &error);
if (moduleJ) {
fromJson(moduleJ);
json_decref(moduleJ);
}
else {
std::string message = string::f("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
if (!moduleJ) {
std::string message = string::f("File is not a valid patch file. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
return;
}
DEFER({
json_decref(moduleJ);
});

fclose(file);
fromJson(moduleJ);
}

void ModuleWidget::save(std::string filename) {
INFO("Saving preset %s", filename.c_str());

json_t *moduleJ = toJson();
if (!moduleJ)
return;
assert(moduleJ);
DEFER({
json_decref(moduleJ);
});

FILE *file = fopen(filename.c_str(), "w");
if (file) {
json_dumpf(moduleJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
fclose(file);
if (!file) {
WARN("Could not write to patch file %s", filename.c_str());
}
DEFER({
fclose(file);
});

json_decref(moduleJ);
json_dumpf(moduleJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
}

void ModuleWidget::loadDialog() {
@@ -189,12 +207,20 @@ void ModuleWidget::loadDialog() {
system::createDirectory(dir);

osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str());
DEFER({
osdialog_filters_free(filters);
});

char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters);
if (path) {
load(path);
free(path);
if (!path) {
// No path selected
return;
}
osdialog_filters_free(filters);
DEFER({
free(path);
});

load(path);
}

void ModuleWidget::saveDialog() {
@@ -202,19 +228,26 @@ void ModuleWidget::saveDialog() {
system::createDirectory(dir);

osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str());
char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters);
DEFER({
osdialog_filters_free(filters);
});

if (path) {
std::string pathStr = path;
char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters);
if (!path) {
// No path selected
return;
}
DEFER({
free(path);
std::string extension = string::extension(pathStr);
if (extension.empty()) {
pathStr += ".vcvm";
}
});

save(pathStr);
std::string pathStr = path;
std::string extension = string::extension(pathStr);
if (extension.empty()) {
pathStr += ".vcvm";
}
osdialog_filters_free(filters);

save(pathStr);
}

void ModuleWidget::disconnect() {
@@ -226,25 +259,13 @@ void ModuleWidget::disconnect() {
}
}

void ModuleWidget::create() {
}

void ModuleWidget::_delete() {
}

void ModuleWidget::reset() {
for (ParamWidget *param : params) {
param->reset();
}
if (module) {
context()->engine->resetModule(module);
}
}

void ModuleWidget::randomize() {
for (ParamWidget *param : params) {
param->randomize();
}
if (module) {
context()->engine->randomizeModule(module);
}
@@ -300,9 +321,7 @@ void ModuleWidget::onHover(event::Hover &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.
if (glfwGetKey(context()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(context()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) {
if (!context()->window->isModPressed() && !context()->window->isShiftPressed()) {
context()->scene->rackWidget->deleteModule(this);
delete this;
// e.target = this;
requestedDelete = true;
return;
}
}
@@ -365,7 +384,6 @@ void ModuleWidget::onHoverKey(event::HoverKey &e) {

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

void ModuleWidget::onDragEnd(event::DragEnd &e) {
@@ -375,6 +393,7 @@ void ModuleWidget::onDragMove(event::DragMove &e) {
if (!settings::lockModules) {
math::Rect newBox = box;
newBox.pos = context()->scene->rackWidget->lastMousePos.minus(dragPos);
DEBUG("%f %f", newBox.pos.x, newBox.pos.y);
context()->scene->rackWidget->requestModuleBoxNearest(this, newBox);
}
}
@@ -382,6 +401,10 @@ void ModuleWidget::onDragMove(event::DragMove &e) {

struct ModuleDisconnectItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleDisconnectItem() {
text = "Disconnect cables";
rightText = WINDOW_MOD_KEY_NAME "+U";
}
void onAction(event::Action &e) override {
moduleWidget->disconnect();
}
@@ -389,6 +412,10 @@ struct ModuleDisconnectItem : MenuItem {

struct ModuleResetItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleResetItem() {
text = "Initialize";
rightText = WINDOW_MOD_KEY_NAME "+I";
}
void onAction(event::Action &e) override {
moduleWidget->reset();
}
@@ -396,6 +423,10 @@ struct ModuleResetItem : MenuItem {

struct ModuleRandomizeItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleRandomizeItem() {
text = "Randomize";
rightText = WINDOW_MOD_KEY_NAME "+R";
}
void onAction(event::Action &e) override {
moduleWidget->randomize();
}
@@ -403,6 +434,10 @@ struct ModuleRandomizeItem : MenuItem {

struct ModuleCopyItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleCopyItem() {
text = "Copy preset";
rightText = WINDOW_MOD_KEY_NAME "+C";
}
void onAction(event::Action &e) override {
moduleWidget->copyClipboard();
}
@@ -410,6 +445,10 @@ struct ModuleCopyItem : MenuItem {

struct ModulePasteItem : MenuItem {
ModuleWidget *moduleWidget;
ModulePasteItem() {
text = "Paste preset";
rightText = WINDOW_MOD_KEY_NAME "+V";
}
void onAction(event::Action &e) override {
moduleWidget->pasteClipboard();
}
@@ -417,6 +456,9 @@ struct ModulePasteItem : MenuItem {

struct ModuleSaveItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleSaveItem() {
text = "Save preset";
}
void onAction(event::Action &e) override {
moduleWidget->saveDialog();
}
@@ -424,6 +466,9 @@ struct ModuleSaveItem : MenuItem {

struct ModuleLoadItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleLoadItem() {
text = "Load preset";
}
void onAction(event::Action &e) override {
moduleWidget->loadDialog();
}
@@ -431,6 +476,10 @@ struct ModuleLoadItem : MenuItem {

struct ModuleCloneItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleCloneItem() {
text = "Duplicate";
rightText = WINDOW_MOD_KEY_NAME "+D";
}
void onAction(event::Action &e) override {
context()->scene->rackWidget->cloneModule(moduleWidget);
}
@@ -438,6 +487,10 @@ struct ModuleCloneItem : MenuItem {

struct ModuleDeleteItem : MenuItem {
ModuleWidget *moduleWidget;
ModuleDeleteItem() {
text = "Delete";
rightText = "Backspace/Delete";
}
void onAction(event::Action &e) override {
context()->scene->rackWidget->deleteModule(moduleWidget);
delete moduleWidget;
@@ -452,54 +505,38 @@ Menu *ModuleWidget::createContextMenu() {
menu->addChild(menuLabel);

ModuleResetItem *resetItem = new ModuleResetItem;
resetItem->text = "Initialize";
resetItem->rightText = WINDOW_MOD_KEY_NAME "+I";
resetItem->moduleWidget = this;
menu->addChild(resetItem);

ModuleRandomizeItem *randomizeItem = new ModuleRandomizeItem;
randomizeItem->text = "Randomize";
randomizeItem->rightText = WINDOW_MOD_KEY_NAME "+R";
randomizeItem->moduleWidget = this;
menu->addChild(randomizeItem);

ModuleDisconnectItem *disconnectItem = new ModuleDisconnectItem;
disconnectItem->text = "Disconnect cables";
disconnectItem->rightText = WINDOW_MOD_KEY_NAME "+U";
disconnectItem->moduleWidget = this;
menu->addChild(disconnectItem);

ModuleCloneItem *cloneItem = new ModuleCloneItem;
cloneItem->text = "Duplicate";
cloneItem->rightText = WINDOW_MOD_KEY_NAME "+D";
cloneItem->moduleWidget = this;
menu->addChild(cloneItem);

ModuleCopyItem *copyItem = new ModuleCopyItem;
copyItem->text = "Copy preset";
copyItem->rightText = WINDOW_MOD_KEY_NAME "+C";
copyItem->moduleWidget = this;
menu->addChild(copyItem);

ModulePasteItem *pasteItem = new ModulePasteItem;
pasteItem->text = "Paste preset";
pasteItem->rightText = WINDOW_MOD_KEY_NAME "+V";
pasteItem->moduleWidget = this;
menu->addChild(pasteItem);

ModuleLoadItem *loadItem = new ModuleLoadItem;
loadItem->text = "Load preset";
loadItem->moduleWidget = this;
menu->addChild(loadItem);

ModuleSaveItem *saveItem = new ModuleSaveItem;
saveItem->text = "Save preset";
saveItem->moduleWidget = this;
menu->addChild(saveItem);

ModuleDeleteItem *deleteItem = new ModuleDeleteItem;
deleteItem->text = "Delete";
deleteItem->rightText = "Backspace/Delete";
deleteItem->moduleWidget = this;
menu->addChild(deleteItem);



+ 7
- 21
src/app/ParamWidget.cpp View File

@@ -13,31 +13,17 @@ void ParamWidget::fromJson(json_t *rootJ) {
}
}

void ParamWidget::reset() {
if (quantity) {
// Infinite params should not be reset
if (quantity->isBounded())
void ParamWidget::onButton(event::Button &e) {
// Right click to reset
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
if (quantity)
quantity->reset();
// Here's another way of doing it, but either works.
// dynamic_cast<ParamQuantity*>(quantity)->getParam()->reset();
return;
}
}

void ParamWidget::randomize() {
if (quantity) {
// Infinite params should not be randomized
if (quantity->isBounded()) {
quantity->setScaledValue(random::uniform());
}
}
}

void ParamWidget::onButton(event::Button &e) {
OpaqueWidget::onButton(e);
if (e.target == this) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
if (quantity)
quantity->reset();
}
}
}




+ 8
- 4
src/app/RackWidget.cpp View File

@@ -17,13 +17,14 @@ namespace rack {

struct ModuleContainer : Widget {
void draw(NVGcontext *vg) override {
// Draw shadows behind each ModuleWidget first, so the shadow doesn't overlap the front.
// Draw shadows behind each ModuleWidget first, so the shadow doesn't overlap the front of other ModuleWidgets.
for (Widget *child : children) {
if (!child->visible)
continue;
nvgSave(vg);
nvgTranslate(vg, child->box.pos.x, child->box.pos.y);
ModuleWidget *w = dynamic_cast<ModuleWidget*>(child);
assert(w);
w->drawShadow(vg);
nvgRestore(vg);
}
@@ -415,16 +416,14 @@ void RackWidget::pastePresetClipboard() {

void RackWidget::addModule(ModuleWidget *m) {
moduleContainer->addChild(m);
m->create();
}

void RackWidget::deleteModule(ModuleWidget *m) {
m->_delete();
moduleContainer->removeChild(m);
}

void RackWidget::cloneModule(ModuleWidget *m) {
// JSON serialization is the most straightforward way to do this
// JSON serialization is the obvious way to do this
json_t *moduleJ = m->toJson();
ModuleWidget *clonedModuleWidget = moduleFromJson(moduleJ);
json_decref(moduleJ);
@@ -509,6 +508,11 @@ void RackWidget::onHover(event::Hover &e) {
lastMousePos = e.pos;
}

void RackWidget::onDragHover(event::DragHover &e) {
OpaqueWidget::onDragHover(e);
lastMousePos = e.pos;
}

void RackWidget::onButton(event::Button &e) {
OpaqueWidget::onButton(e);
if (e.target == this) {


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

@@ -336,7 +336,7 @@ struct ManageItem : MenuItem {

struct SyncItem : MenuItem {
SyncItem() {
text = "Sync";
text = "Sync plugins";
disabled = true;
}
void onAction(event::Action &e) override {


+ 2
- 2
src/engine/Engine.cpp View File

@@ -93,11 +93,11 @@ static void Engine_step(Engine *engine) {

// Events
if (engine->internal->resetModule) {
engine->internal->resetModule->onReset();
engine->internal->resetModule->reset();
engine->internal->resetModule = NULL;
}
if (engine->internal->randomizeModule) {
engine->internal->randomizeModule->onRandomize();
engine->internal->randomizeModule->randomize();
engine->internal->randomizeModule = NULL;
}



+ 14
- 0
src/engine/Module.cpp View File

@@ -50,5 +50,19 @@ void Module::fromJson(json_t *rootJ) {
}
}

void Module::reset() {
for (Param &param : params) {
param.reset();
}
onReset();
}

void Module::randomize() {
for (Param &param : params) {
param.randomize();
}
onRandomize();
}


} // namespace rack

+ 14
- 0
src/engine/Param.cpp View File

@@ -1,4 +1,6 @@
#include "engine/Param.hpp"
#include "random.hpp"
#include "math.hpp"


namespace rack {
@@ -20,5 +22,17 @@ void Param::fromJson(json_t *rootJ) {
value = json_number_value(valueJ);
}

void Param::reset() {
if (std::isfinite(minValue) && std::isfinite(maxValue)) {
value = defaultValue;
}
}

void Param::randomize() {
if (std::isfinite(minValue) && std::isfinite(maxValue)) {
value = math::rescale(random::uniform(), 0.f, 1.f, minValue, maxValue);
}
}


} // namespace rack

Loading…
Cancel
Save