Browse Source

Add actions to ModuleWidget::createSelectionContextMenu(). Refactor RackWidget and ModuleWidget as necessary.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
bcc22eba61
5 changed files with 255 additions and 48 deletions
  1. +10
    -1
      include/app/ModuleWidget.hpp
  2. +18
    -7
      include/app/RackWidget.hpp
  3. +101
    -32
      src/app/ModuleWidget.cpp
  4. +125
    -7
      src/app/RackWidget.cpp
  5. +1
    -1
      src/patch.cpp

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

@@ -6,6 +6,8 @@
#include <app/ParamWidget.hpp>
#include <plugin/Model.hpp>
#include <engine/Module.hpp>
#include <history.hpp>
#include <list>


namespace rack {
@@ -54,12 +56,17 @@ struct ModuleWidget : widget::OpaqueWidget {
ParamWidget* getParam(int paramId);
PortWidget* getInput(int portId);
PortWidget* getOutput(int portId);
/** Scans children widgets recursively for all ParamWidgets. */
std::list<ParamWidget*> getParams();
std::list<PortWidget*> getPorts();
std::list<PortWidget*> getInputs();
std::list<PortWidget*> getOutputs();

void draw(const DrawArgs& args) override;
void drawShadow(const DrawArgs& args);

/** Override to add context menu entries to your subclass.
It is recommended to add a blank ui::MenuEntry first for spacing.
It is recommended to add a blank `ui::MenuSeparator` first for spacing.
*/
virtual void appendContextMenu(ui::Menu* menu) {}

@@ -98,12 +105,14 @@ struct ModuleWidget : widget::OpaqueWidget {
Called when the user clicks Randomize in the context menu.
*/
void randomizeAction();
void appendDisconnectActions(history::ComplexAction* complexAction);
void disconnectAction();
void cloneAction();
void bypassAction();
/** Deletes `this` */
void removeAction();
void createContextMenu();
void createSelectionContextMenu();

INTERNAL math::Vec& dragOffset();
INTERNAL bool& dragEnabled();


+ 18
- 7
include/app/RackWidget.hpp View File

@@ -52,12 +52,12 @@ struct RackWidget : widget::OpaqueWidget {

// Module methods

/** Adds a module and adds it to the Engine
Ownership rules work like add/removeChild()
/** Adds a module and adds it to the Engine, adopting ownership.
*/
void addModule(ModuleWidget* mw);
void addModuleAtMouse(ModuleWidget* mw);
/** Removes the module and transfers ownership to the caller */
/** Removes the module and transfers ownership to the caller.
*/
void removeModule(ModuleWidget* mw);
/** Sets a module's box if non-colliding. Returns true if set */
bool requestModulePos(ModuleWidget* mw, math::Vec pos);
@@ -65,21 +65,31 @@ struct RackWidget : widget::OpaqueWidget {
void setModulePosNearest(ModuleWidget* mw, math::Vec pos);
void setModulePosForce(ModuleWidget* mw, math::Vec pos);
ModuleWidget* getModule(int64_t moduleId);
bool isEmpty();
std::list<ModuleWidget*> getModules();
bool hasModules();
void updateModuleOldPositions();
history::ComplexAction* getModuleDragAction();

void updateModuleSelections();
int getNumSelectedModules();
std::list<ModuleWidget*> getSelectedModules();
void resetSelectedModulesAction();
void randomizeSelectedModulesAction();
void disconnectSelectedModulesAction();
void bypassSelectedModulesAction();
void deleteSelectedModulesAction();

// Cable methods

void clearCables();
void clearCablesAction();
/** Removes all complete cables connected to the port */
/** Removes all cables connected to the port */
void clearCablesOnPort(PortWidget* port);
/** Adds a complete cable.
Ownership rules work like add/removeChild()
/** Adds a complete cable and adopts ownership.
*/
void addCable(CableWidget* cw);
/** Removes cable and releases ownership to caller.
*/
void removeCable(CableWidget* cw);
/** Takes ownership of `cw` and adds it as a child if it isn't already. */
void setIncompleteCable(CableWidget* cw);
@@ -87,6 +97,7 @@ struct RackWidget : widget::OpaqueWidget {
/** 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(int64_t cableId);
std::list<CableWidget*> getCompleteCables();
/** Returns all cables attached to port, complete or not. */
std::list<CableWidget*> getCablesOnPort(PortWidget* port);
};


+ 101
- 32
src/app/ModuleWidget.cpp View File

@@ -159,6 +159,51 @@ PortWidget* ModuleWidget::getOutput(int portId) {
});
}

template <class T, typename F>
void doIfTypeRecursive(widget::Widget* w, F f) {
T* t = dynamic_cast<T*>(w);
if (t)
f(t);

for (widget::Widget* child : w->children) {
doIfTypeRecursive<T>(child, f);
}
}

std::list<ParamWidget*> ModuleWidget::getParams() {
std::list<ParamWidget*> pws;
doIfTypeRecursive<ParamWidget>(this, [&](ParamWidget* pw) {
pws.push_back(pw);
});
return pws;
}

std::list<PortWidget*> ModuleWidget::getPorts() {
std::list<PortWidget*> pws;
doIfTypeRecursive<PortWidget>(this, [&](PortWidget* pw) {
pws.push_back(pw);
});
return pws;
}

std::list<PortWidget*> ModuleWidget::getInputs() {
std::list<PortWidget*> pws;
doIfTypeRecursive<PortWidget>(this, [&](PortWidget* pw) {
if (pw->type == engine::Port::INPUT)
pws.push_back(pw);
});
return pws;
}

std::list<PortWidget*> ModuleWidget::getOutputs() {
std::list<PortWidget*> pws;
doIfTypeRecursive<PortWidget>(this, [&](PortWidget* pw) {
if (pw->type == engine::Port::OUTPUT)
pws.push_back(pw);
});
return pws;
}

void ModuleWidget::draw(const DrawArgs& args) {
nvgScissor(args.vg, RECT_ARGS(args.clipBox));

@@ -325,7 +370,12 @@ void ModuleWidget::onButton(const ButtonEvent& e) {
if (!e.isConsumed()) {
// Open context menu on right-click
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
createContextMenu();
if (internal->selected) {
createSelectionContextMenu();
}
else {
createContextMenu();
}
e.consume(this);
}
}
@@ -615,21 +665,10 @@ void ModuleWidget::saveDialog() {
save(path);
}

template <class T, typename F>
void doOfType(widget::Widget* w, F f) {
T* t = dynamic_cast<T*>(w);
if (t)
f(t);

for (widget::Widget* child : w->children) {
doOfType<T>(child, f);
}
}

void ModuleWidget::disconnect() {
doOfType<PortWidget>(this, [&](PortWidget* pw) {
for (PortWidget* pw : getPorts()) {
APP->scene->rack->clearCablesOnPort(pw);
});
}
}

void ModuleWidget::resetAction() {
@@ -662,30 +701,31 @@ void ModuleWidget::randomizeAction() {
APP->history->push(h);
}

static void disconnectActions(ModuleWidget* mw, history::ComplexAction* complexAction) {
// Add CableRemove action for all cables
doOfType<PortWidget>(mw, [&](PortWidget* pw) {
void ModuleWidget::appendDisconnectActions(history::ComplexAction* complexAction) {
for (PortWidget* pw : getPorts()) {
for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) {
if (!cw->isComplete())
continue;
// Avoid creating duplicate actions for self-patched cables
if (pw->type == engine::Port::INPUT && cw->outputPort->module == mw->module)
continue;
// history::CableRemove
history::CableRemove* h = new history::CableRemove;
h->setCable(cw);
complexAction->push(h);
// Delete cable
APP->scene->rack->removeCable(cw);
delete cw;
}
});
};
}

void ModuleWidget::disconnectAction() {
history::ComplexAction* complexAction = new history::ComplexAction;
complexAction->name = "disconnect cables";
disconnectActions(this, complexAction);
APP->history->push(complexAction);
appendDisconnectActions(complexAction);

disconnect();
if (!complexAction->isEmpty())
APP->history->push(complexAction);
else
delete complexAction;
}

void ModuleWidget::cloneAction() {
@@ -722,11 +762,8 @@ void ModuleWidget::cloneAction() {
h->push(hma);

// Clone cables attached to input ports
doOfType<PortWidget>(this, [&](PortWidget* pw) {
if (pw->type != engine::Port::INPUT)
return;
std::list<CableWidget*> cables = APP->scene->rack->getCablesOnPort(pw);
for (CableWidget* cw : cables) {
for (PortWidget* pw : getInputs()) {
for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) {
// Create cable attached to cloned ModuleWidget's input
engine::Cable* clonedCable = new engine::Cable;
clonedCable->id = -1;
@@ -750,7 +787,7 @@ void ModuleWidget::cloneAction() {
hca->setCable(clonedCw);
h->push(hca);
}
});
}

APP->history->push(h);
}
@@ -769,7 +806,7 @@ void ModuleWidget::bypassAction() {
void ModuleWidget::removeAction() {
history::ComplexAction* complexAction = new history::ComplexAction;
complexAction->name = "remove module";
disconnectActions(this, complexAction);
appendDisconnectActions(complexAction);

// history::ModuleRemove
history::ModuleRemove* moduleRemove = new history::ModuleRemove;
@@ -778,7 +815,7 @@ void ModuleWidget::removeAction() {

APP->history->push(complexAction);

// This disconnects cables, removes the module, and transfers ownership to caller
// This removes the module and transfers ownership to caller
APP->scene->rack->removeModule(this);
delete this;
}
@@ -1018,6 +1055,38 @@ void ModuleWidget::createContextMenu() {
appendContextMenu(menu);
}

void ModuleWidget::createSelectionContextMenu() {
ui::Menu* menu = createMenu();

int n = APP->scene->rack->getNumSelectedModules();
menu->addChild(createMenuLabel(string::f("%d selected %s", n, n == 1 ? "module" : "modules")));

// Initialize
menu->addChild(createMenuItem("Initialize", "", [=]() {
APP->scene->rack->resetSelectedModulesAction();
}));

// Randomize
menu->addChild(createMenuItem("Randomize", "", [=]() {
APP->scene->rack->randomizeSelectedModulesAction();
}));

// Disconnect cables
menu->addChild(createMenuItem("Disconnect cables", "", [=]() {
APP->scene->rack->disconnectSelectedModulesAction();
}));

// Bypass
menu->addChild(createMenuItem("Bypass", "", [=]() {
APP->scene->rack->bypassSelectedModulesAction();
}));

// Delete
menu->addChild(createMenuItem("Delete", "", [=]() {
APP->scene->rack->deleteSelectedModulesAction();
}));
}

math::Vec& ModuleWidget::dragOffset() {
return internal->dragOffset;
}


+ 125
- 7
src/app/RackWidget.cpp View File

@@ -612,7 +612,17 @@ ModuleWidget* RackWidget::getModule(int64_t moduleId) {
return NULL;
}

bool RackWidget::isEmpty() {
std::list<ModuleWidget*> RackWidget::getModules() {
std::list<ModuleWidget*> mws;
for (widget::Widget* w : internal->moduleContainer->children) {
ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
assert(mw);
mws.push_back(mw);
}
return mws;
}

bool RackWidget::hasModules() {
return internal->moduleContainer->children.empty();
}

@@ -659,6 +669,107 @@ void RackWidget::updateModuleSelections() {
}
}

int RackWidget::getNumSelectedModules() {
int count = 0;
for (widget::Widget* w : internal->moduleContainer->children) {
ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
assert(mw);
if (mw->selected())
count++;
}
return count;
}

std::list<ModuleWidget*> RackWidget::getSelectedModules() {
std::list<ModuleWidget*> mws;
for (widget::Widget* w : internal->moduleContainer->children) {
ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
assert(mw);
if (mw->selected())
mws.push_back(mw);
}
return mws;
}

void RackWidget::resetSelectedModulesAction() {
history::ComplexAction* complexAction = new history::ComplexAction;
complexAction->name = "reset modules";

for (ModuleWidget* mw : getSelectedModules()) {
assert(mw->module);

// history::ModuleChange
history::ModuleChange* h = new history::ModuleChange;
h->moduleId = mw->module->id;
h->oldModuleJ = mw->toJson();

APP->engine->resetModule(mw->module);

h->newModuleJ = mw->toJson();
complexAction->push(h);
}

APP->history->push(complexAction);
}

void RackWidget::randomizeSelectedModulesAction() {
history::ComplexAction* complexAction = new history::ComplexAction;
complexAction->name = "randomize modules";

for (ModuleWidget* mw : getSelectedModules()) {
assert(mw->module);

// history::ModuleChange
history::ModuleChange* h = new history::ModuleChange;
h->moduleId = mw->module->id;
h->oldModuleJ = mw->toJson();

APP->engine->randomizeModule(mw->module);

h->newModuleJ = mw->toJson();
complexAction->push(h);
}

APP->history->push(complexAction);
}

void RackWidget::disconnectSelectedModulesAction() {
history::ComplexAction* complexAction = new history::ComplexAction;
complexAction->name = "disconnect cables";

for (ModuleWidget* mw : getSelectedModules()) {
mw->appendDisconnectActions(complexAction);
}

if (!complexAction->isEmpty())
APP->history->push(complexAction);
else
delete complexAction;
}

void RackWidget::bypassSelectedModulesAction() {
// TODO
}

void RackWidget::deleteSelectedModulesAction() {
history::ComplexAction* complexAction = new history::ComplexAction;
complexAction->name = "remove modules";

for (ModuleWidget* mw : getSelectedModules()) {
mw->appendDisconnectActions(complexAction);

// history::ModuleRemove
history::ModuleRemove* moduleRemove = new history::ModuleRemove;
moduleRemove->setModule(mw);
complexAction->push(moduleRemove);

removeModule(mw);
delete mw;
}

APP->history->push(complexAction);
}

void RackWidget::clearCables() {
incompleteCable = NULL;
internal->cableContainer->clearChildren();
@@ -669,12 +780,7 @@ void RackWidget::clearCablesAction() {
history::ComplexAction* complexAction = new history::ComplexAction;
complexAction->name = "clear cables";

for (widget::Widget* w : internal->cableContainer->children) {
CableWidget* cw = dynamic_cast<CableWidget*>(w);
assert(cw);
if (!cw->isComplete())
continue;

for (CableWidget* cw : getCompleteCables()) {
// history::CableRemove
history::CableRemove* h = new history::CableRemove;
h->setCable(cw);
@@ -685,6 +791,7 @@ void RackWidget::clearCablesAction() {
APP->history->push(complexAction);
else
delete complexAction;

clearCables();
}

@@ -756,6 +863,17 @@ CableWidget* RackWidget::getCable(int64_t cableId) {
return NULL;
}

std::list<CableWidget*> RackWidget::getCompleteCables() {
std::list<CableWidget*> cws;
for (widget::Widget* w : internal->cableContainer->children) {
CableWidget* cw = dynamic_cast<CableWidget*>(w);
assert(cw);
if (cw->isComplete())
cws.push_back(cw);
}
return cws;
}

std::list<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) {
assert(port);
std::list<CableWidget*> cws;


+ 1
- 1
src/patch.cpp View File

@@ -79,7 +79,7 @@ void PatchManager::clear() {
static bool promptClear(std::string text) {
if (APP->history->isSaved())
return true;
if (APP->scene->rack->isEmpty())
if (APP->scene->rack->hasModules())
return true;
return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str());
}


Loading…
Cancel
Save