#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace rack { /** Returns a Model that constructs a Module and ModuleWidget subclass. */ template plugin::Model* createModel(std::string slug) { struct TModel : plugin::Model { engine::Module* createModule() override { engine::Module* m = new TModule; m->model = this; return m; } app::ModuleWidget* createModuleWidget(engine::Module* m) override { TModule* tm = NULL; if (m) { assert(m->model == this); tm = dynamic_cast(m); } app::ModuleWidget* mw = new TModuleWidget(tm); assert(mw->module == m); mw->setModel(this); return mw; } }; plugin::Model* o = new TModel; o->slug = slug; return o; } /** Creates a Widget subclass with its top-left at a position. */ template TWidget* createWidget(math::Vec pos) { TWidget* o = new TWidget; o->box.pos = pos; return o; } /** Creates a Widget subclass with its center at a position. */ template TWidget* createWidgetCentered(math::Vec pos) { TWidget* o = createWidget(pos); o->box.pos = o->box.pos.minus(o->box.size.div(2)); return o; } /** Creates an SvgPanel and loads the SVG from the given path. */ template TPanel* createPanel(std::string svgPath) { TPanel* panel = new TPanel; panel->setBackground(window::Svg::load(svgPath)); return panel; } /** Creates a ThemedSvgPanel and loads the light/dark SVGs from the given paths. */ template TPanel* createPanel(std::string lightSvgPath, std::string darkSvgPath) { TPanel* panel = new TPanel; panel->setBackground(window::Svg::load(lightSvgPath), window::Svg::load(darkSvgPath)); return panel; } template TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { TParamWidget* o = new TParamWidget; o->box.pos = pos; o->app::ParamWidget::module = module; o->app::ParamWidget::paramId = paramId; o->initParamQuantity(); return o; } template TParamWidget* createParamCentered(math::Vec pos, engine::Module* module, int paramId) { TParamWidget* o = createParam(pos, module, paramId); o->box.pos = o->box.pos.minus(o->box.size.div(2)); return o; } template TPortWidget* createInput(math::Vec pos, engine::Module* module, int inputId) { TPortWidget* o = new TPortWidget; o->box.pos = pos; o->app::PortWidget::module = module; o->app::PortWidget::type = engine::Port::INPUT; o->app::PortWidget::portId = inputId; return o; } template TPortWidget* createInputCentered(math::Vec pos, engine::Module* module, int inputId) { TPortWidget* o = createInput(pos, module, inputId); o->box.pos = o->box.pos.minus(o->box.size.div(2)); return o; } template TPortWidget* createOutput(math::Vec pos, engine::Module* module, int outputId) { TPortWidget* o = new TPortWidget; o->box.pos = pos; o->app::PortWidget::module = module; o->app::PortWidget::type = engine::Port::OUTPUT; o->app::PortWidget::portId = outputId; return o; } template TPortWidget* createOutputCentered(math::Vec pos, engine::Module* module, int outputId) { TPortWidget* o = createOutput(pos, module, outputId); o->box.pos = o->box.pos.minus(o->box.size.div(2)); return o; } template TModuleLightWidget* createLight(math::Vec pos, engine::Module* module, int firstLightId) { TModuleLightWidget* o = new TModuleLightWidget; o->box.pos = pos; o->app::ModuleLightWidget::module = module; o->app::ModuleLightWidget::firstLightId = firstLightId; return o; } template TModuleLightWidget* createLightCentered(math::Vec pos, engine::Module* module, int firstLightId) { TModuleLightWidget* o = createLight(pos, module, firstLightId); o->box.pos = o->box.pos.minus(o->box.size.div(2)); return o; } /** Creates a param with a light and calls setFirstLightId() on it. Requires ParamWidget to have a `light` member. */ template TParamWidget* createLightParam(math::Vec pos, engine::Module* module, int paramId, int firstLightId) { TParamWidget* o = createParam(pos, module, paramId); o->getLight()->module = module; o->getLight()->firstLightId = firstLightId; return o; } template TParamWidget* createLightParamCentered(math::Vec pos, engine::Module* module, int paramId, int firstLightId) { TParamWidget* o = createLightParam(pos, module, paramId, firstLightId); o->box.pos = o->box.pos.minus(o->box.size.div(2)); return o; } template TMenu* createMenu() { TMenu* menu = new TMenu; menu->box.pos = APP->scene->mousePos; ui::MenuOverlay* menuOverlay = new ui::MenuOverlay; menuOverlay->addChild(menu); APP->scene->addChild(menuOverlay); return menu; } template TMenuLabel* createMenuLabel(std::string text) { TMenuLabel* label = new TMenuLabel; label->text = text; return label; } template TMenuItem* createMenuItem(std::string text, std::string rightText = "") { TMenuItem* item = new TMenuItem; item->text = text; item->rightText = rightText; return item; } /** Creates a MenuItem with an action that calls a lambda function. Example: menu->addChild(createMenuItem("Load sample", "kick.wav", [=]() { module->loadSample(); } )); */ template TMenuItem* createMenuItem(std::string text, std::string rightText, std::function action, bool disabled = false, bool alwaysConsume = false) { struct Item : TMenuItem { std::function action; bool alwaysConsume; void onAction(const event::Action& e) override { action(); if (alwaysConsume) e.consume(this); } }; Item* item = createMenuItem(text, rightText); item->action = action; item->disabled = disabled; item->alwaysConsume = alwaysConsume; return item; } /** Creates a MenuItem with a check mark set by a lambda function. Example: menu->addChild(createCheckMenuItem("Loop", "", [=]() { return module->isLoop(); }, [=]() { module->toggleLoop(); } )); */ template TMenuItem* createCheckMenuItem(std::string text, std::string rightText, std::function checked, std::function action, bool disabled = false, bool alwaysConsume = false) { struct Item : TMenuItem { std::string rightTextPrefix; std::function checked; std::function action; bool alwaysConsume; void step() override { this->rightText = rightTextPrefix; if (checked()) { if (!rightTextPrefix.empty()) this->rightText += " "; this->rightText += CHECKMARK_STRING; } TMenuItem::step(); } void onAction(const event::Action& e) override { action(); if (alwaysConsume) e.consume(this); } }; Item* item = createMenuItem(text); item->rightTextPrefix = rightText; item->checked = checked; item->action = action; item->disabled = disabled; item->alwaysConsume = alwaysConsume; return item; } /** Creates a MenuItem that controls a boolean value with a check mark. Example: menu->addChild(createBoolMenuItem("Loop", "", [=]() { return module->isLoop(); }, [=](bool loop) { module->setLoop(loop); } )); */ template TMenuItem* createBoolMenuItem(std::string text, std::string rightText, std::function getter, std::function setter, bool disabled = false, bool alwaysConsume = false) { struct Item : TMenuItem { std::string rightTextPrefix; std::function getter; std::function setter; bool alwaysConsume; void step() override { this->rightText = rightTextPrefix; if (getter()) { if (!rightTextPrefix.empty()) this->rightText += " "; this->rightText += CHECKMARK_STRING; } TMenuItem::step(); } void onAction(const event::Action& e) override { setter(!getter()); if (alwaysConsume) e.consume(this); } }; Item* item = createMenuItem(text); item->rightTextPrefix = rightText; item->getter = getter; item->setter = setter; item->disabled = disabled; item->alwaysConsume = alwaysConsume; return item; } /** Easy wrapper for createBoolMenuItem() to modify a bool pointer. Example: menu->addChild(createBoolPtrMenuItem("Loop", "", &module->loop)); */ template ui::MenuItem* createBoolPtrMenuItem(std::string text, std::string rightText, T* ptr) { return createBoolMenuItem(text, rightText, [=]() { return ptr ? *ptr : false; }, [=](T val) { if (ptr) *ptr = val; } ); } /** Creates a MenuItem that opens a submenu. Example: menu->addChild(createSubmenuItem("Edit", "", [=](Menu* menu) { menu->addChild(createMenuItem("Copy", "", [=]() {copy();})); menu->addChild(createMenuItem("Paste", "", [=]() {paste();})); } )); */ template TMenuItem* createSubmenuItem(std::string text, std::string rightText, std::function createMenu, bool disabled = false) { struct Item : TMenuItem { std::function createMenu; ui::Menu* createChildMenu() override { ui::Menu* menu = new ui::Menu; createMenu(menu); return menu; } }; Item* item = createMenuItem(text, rightText + (rightText.empty() ? "" : " ") + RIGHT_ARROW); item->createMenu = createMenu; item->disabled = disabled; return item; } /** Creates a MenuItem that when hovered, opens a submenu with several MenuItems indexed by an integer. Example: menu->addChild(createIndexSubmenuItem("Mode", {"Hi-fi", "Mid-fi", "Lo-fi"}, [=]() { return module->getMode(); }, [=](int mode) { module->setMode(mode); } )); */ template TMenuItem* createIndexSubmenuItem(std::string text, std::vector labels, std::function getter, std::function setter, bool disabled = false, bool alwaysConsume = false) { struct IndexItem : ui::MenuItem { std::function getter; std::function setter; size_t index; bool alwaysConsume; void step() override { size_t currIndex = getter(); this->rightText = CHECKMARK(currIndex == index); MenuItem::step(); } void onAction(const event::Action& e) override { setter(index); if (alwaysConsume) e.consume(this); } }; struct Item : TMenuItem { std::function getter; std::function setter; std::vector labels; bool alwaysConsume; void step() override { size_t currIndex = getter(); std::string label = (currIndex < labels.size()) ? labels[currIndex] : ""; this->rightText = label + " " + RIGHT_ARROW; TMenuItem::step(); } ui::Menu* createChildMenu() override { ui::Menu* menu = new ui::Menu; for (size_t i = 0; i < labels.size(); i++) { IndexItem* item = createMenuItem(labels[i]); item->getter = getter; item->setter = setter; item->index = i; item->alwaysConsume = alwaysConsume; menu->addChild(item); } return menu; } }; Item* item = createMenuItem(text); item->getter = getter; item->setter = setter; item->labels = labels; item->disabled = disabled; item->alwaysConsume = alwaysConsume; return item; } /** Easy wrapper for createIndexSubmenuItem() that controls an integer index at a pointer address. Example: menu->addChild(createIndexPtrSubmenuItem("Mode", {"Hi-fi", "Mid-fi", "Lo-fi"}, &module->mode )); */ template ui::MenuItem* createIndexPtrSubmenuItem(std::string text, std::vector labels, T* ptr) { return createIndexSubmenuItem(text, labels, [=]() { return ptr ? *ptr : 0; }, [=](size_t index) { if (ptr) *ptr = T(index); } ); } } // namespace rack