You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

435 lines
11KB

  1. #pragma once
  2. #include <plugin/Model.hpp>
  3. #include <ui/MenuOverlay.hpp>
  4. #include <ui/MenuItem.hpp>
  5. #include <ui/MenuLabel.hpp>
  6. #include <ui/Menu.hpp>
  7. #include <app/PortWidget.hpp>
  8. #include <app/ParamWidget.hpp>
  9. #include <app/ModuleLightWidget.hpp>
  10. #include <app/Scene.hpp>
  11. #include <app/SvgPanel.hpp>
  12. #include <engine/Module.hpp>
  13. #include <engine/ParamQuantity.hpp>
  14. #include <context.hpp>
  15. #include <functional>
  16. namespace rack {
  17. template <class TModule, class TModuleWidget>
  18. plugin::Model* createModel(const std::string& slug) {
  19. struct TModel : plugin::Model {
  20. engine::Module* createModule() override {
  21. engine::Module* m = new TModule;
  22. m->model = this;
  23. return m;
  24. }
  25. app::ModuleWidget* createModuleWidget(engine::Module* m) override {
  26. TModule* tm = NULL;
  27. if (m) {
  28. assert(m->model == this);
  29. tm = dynamic_cast<TModule*>(m);
  30. }
  31. app::ModuleWidget* mw = new TModuleWidget(tm);
  32. mw->setModel(this);
  33. return mw;
  34. }
  35. };
  36. plugin::Model* o = new TModel;
  37. o->slug = slug;
  38. return o;
  39. }
  40. template <class TWidget>
  41. TWidget* createWidget(math::Vec pos) {
  42. TWidget* o = new TWidget;
  43. o->box.pos = pos;
  44. return o;
  45. }
  46. template <class TWidget>
  47. TWidget* createWidgetCentered(math::Vec pos) {
  48. TWidget* o = createWidget<TWidget>(pos);
  49. o->box.pos = o->box.pos.minus(o->box.size.div(2));
  50. return o;
  51. }
  52. inline app::SvgPanel* createPanel(std::string svgPath) {
  53. app::SvgPanel* panel = new app::SvgPanel;
  54. std::shared_ptr<Svg> svg = Svg::load(svgPath);
  55. panel->setBackground(svg);
  56. return panel;
  57. }
  58. template <class TParamWidget>
  59. TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) {
  60. TParamWidget* o = new TParamWidget;
  61. o->box.pos = pos;
  62. o->app::ParamWidget::module = module;
  63. o->app::ParamWidget::paramId = paramId;
  64. o->initParamQuantity();
  65. return o;
  66. }
  67. template <class TParamWidget>
  68. TParamWidget* createParamCentered(math::Vec pos, engine::Module* module, int paramId) {
  69. TParamWidget* o = createParam<TParamWidget>(pos, module, paramId);
  70. o->box.pos = o->box.pos.minus(o->box.size.div(2));
  71. return o;
  72. }
  73. template <class TPortWidget>
  74. TPortWidget* createInput(math::Vec pos, engine::Module* module, int inputId) {
  75. TPortWidget* o = new TPortWidget;
  76. o->box.pos = pos;
  77. o->app::PortWidget::module = module;
  78. o->app::PortWidget::type = engine::Port::INPUT;
  79. o->app::PortWidget::portId = inputId;
  80. return o;
  81. }
  82. template <class TPortWidget>
  83. TPortWidget* createInputCentered(math::Vec pos, engine::Module* module, int inputId) {
  84. TPortWidget* o = createInput<TPortWidget>(pos, module, inputId);
  85. o->box.pos = o->box.pos.minus(o->box.size.div(2));
  86. return o;
  87. }
  88. template <class TPortWidget>
  89. TPortWidget* createOutput(math::Vec pos, engine::Module* module, int outputId) {
  90. TPortWidget* o = new TPortWidget;
  91. o->box.pos = pos;
  92. o->app::PortWidget::module = module;
  93. o->app::PortWidget::type = engine::Port::OUTPUT;
  94. o->app::PortWidget::portId = outputId;
  95. return o;
  96. }
  97. template <class TPortWidget>
  98. TPortWidget* createOutputCentered(math::Vec pos, engine::Module* module, int outputId) {
  99. TPortWidget* o = createOutput<TPortWidget>(pos, module, outputId);
  100. o->box.pos = o->box.pos.minus(o->box.size.div(2));
  101. return o;
  102. }
  103. template <class TModuleLightWidget>
  104. TModuleLightWidget* createLight(math::Vec pos, engine::Module* module, int firstLightId) {
  105. TModuleLightWidget* o = new TModuleLightWidget;
  106. o->box.pos = pos;
  107. o->app::ModuleLightWidget::module = module;
  108. o->app::ModuleLightWidget::firstLightId = firstLightId;
  109. return o;
  110. }
  111. template <class TModuleLightWidget>
  112. TModuleLightWidget* createLightCentered(math::Vec pos, engine::Module* module, int firstLightId) {
  113. TModuleLightWidget* o = createLight<TModuleLightWidget>(pos, module, firstLightId);
  114. o->box.pos = o->box.pos.minus(o->box.size.div(2));
  115. return o;
  116. }
  117. /** Creates a param with a light and calls setFirstLightId() on it.
  118. Requires ParamWidget to have a `light` member.
  119. */
  120. template <class TParamWidget>
  121. TParamWidget* createLightParam(math::Vec pos, engine::Module* module, int paramId, int firstLightId) {
  122. TParamWidget* o = createParam<TParamWidget>(pos, module, paramId);
  123. o->light->module = module;
  124. o->light->firstLightId = firstLightId;
  125. return o;
  126. }
  127. template <class TParamWidget>
  128. TParamWidget* createLightParamCentered(math::Vec pos, engine::Module* module, int paramId, int firstLightId) {
  129. TParamWidget* o = createLightParam<TParamWidget>(pos, module, paramId, firstLightId);
  130. o->box.pos = o->box.pos.minus(o->box.size.div(2));
  131. return o;
  132. }
  133. template <class TMenu = ui::Menu>
  134. TMenu* createMenu() {
  135. TMenu* o = new TMenu;
  136. o->box.pos = APP->scene->mousePos;
  137. ui::MenuOverlay* menuOverlay = new ui::MenuOverlay;
  138. menuOverlay->addChild(o);
  139. APP->scene->addChild(menuOverlay);
  140. return o;
  141. }
  142. template <class TMenuLabel = ui::MenuLabel>
  143. TMenuLabel* createMenuLabel(std::string text) {
  144. TMenuLabel* o = new TMenuLabel;
  145. o->text = text;
  146. return o;
  147. }
  148. template <class TMenuItem = ui::MenuItem>
  149. TMenuItem* createMenuItem(std::string text, std::string rightText = "") {
  150. TMenuItem* o = new TMenuItem;
  151. o->text = text;
  152. o->rightText = rightText;
  153. return o;
  154. }
  155. /** Creates a MenuItem with an action that calls a lambda function.
  156. Example:
  157. menu->addChild(createMenuItem("Load sample", "kick.wav",
  158. [=]() {
  159. module->loadSample();
  160. }
  161. ));
  162. */
  163. template <class TMenuItem = ui::MenuItem>
  164. TMenuItem* createMenuItem(std::string text, std::string rightText, std::function<void()> action, bool disabled = false, bool alwaysConsume = false) {
  165. struct Item : TMenuItem {
  166. std::function<void()> action;
  167. bool alwaysConsume;
  168. void onAction(const event::Action& e) override {
  169. action();
  170. if (alwaysConsume)
  171. e.consume(this);
  172. }
  173. };
  174. Item* item = createMenuItem<Item>(text, rightText);
  175. item->action = action;
  176. item->disabled = disabled;
  177. item->alwaysConsume = alwaysConsume;
  178. return item;
  179. }
  180. /** Creates a MenuItem with a check mark set by a lambda function.
  181. Example:
  182. menu->addChild(createCheckMenuItem("Loop",
  183. [=]() {
  184. return module->isLoop();
  185. },
  186. [=]() {
  187. module->toggleLoop();
  188. }
  189. ));
  190. */
  191. template <class TMenuItem = ui::MenuItem>
  192. ui::MenuItem* createCheckMenuItem(std::string text, std::function<bool()> checked, std::function<void()> action, bool disabled = false, bool alwaysConsume = false) {
  193. struct Item : TMenuItem {
  194. std::function<bool()> checked;
  195. std::function<void()> action;
  196. bool alwaysConsume;
  197. void step() override {
  198. this->rightText = CHECKMARK(checked());
  199. TMenuItem::step();
  200. }
  201. void onAction(const event::Action& e) override {
  202. action();
  203. if (alwaysConsume)
  204. e.consume(this);
  205. }
  206. };
  207. Item* item = createMenuItem<Item>(text);
  208. item->checked = checked;
  209. item->action = action;
  210. item->disabled = disabled;
  211. item->alwaysConsume = alwaysConsume;
  212. return item;
  213. }
  214. /** Creates a MenuItem that controls a boolean value with a check mark.
  215. Example:
  216. menu->addChild(createBoolMenuItem("Loop",
  217. [=]() {
  218. return module->isLoop();
  219. },
  220. [=](bool loop) {
  221. module->setLoop(loop);
  222. }
  223. ));
  224. */
  225. template <class TMenuItem = ui::MenuItem>
  226. ui::MenuItem* createBoolMenuItem(std::string text, std::function<bool()> getter, std::function<void(bool state)> setter, bool disabled = false, bool alwaysConsume = false) {
  227. struct Item : TMenuItem {
  228. std::function<bool()> getter;
  229. std::function<void(size_t)> setter;
  230. bool alwaysConsume;
  231. void step() override {
  232. this->rightText = CHECKMARK(getter());
  233. TMenuItem::step();
  234. }
  235. void onAction(const event::Action& e) override {
  236. setter(!getter());
  237. if (alwaysConsume)
  238. e.consume(this);
  239. }
  240. };
  241. Item* item = createMenuItem<Item>(text);
  242. item->getter = getter;
  243. item->setter = setter;
  244. item->disabled = disabled;
  245. item->alwaysConsume = alwaysConsume;
  246. return item;
  247. }
  248. /** Easy wrapper for createBoolMenuItem() to modify a bool pointer.
  249. Example:
  250. menu->addChild(createBoolPtrMenuItem("Loop", &module->loop));
  251. */
  252. template <typename T>
  253. ui::MenuItem* createBoolPtrMenuItem(std::string text, T* ptr) {
  254. return createBoolMenuItem(text,
  255. [=]() {return *ptr;},
  256. [=](T val) {*ptr = val;}
  257. );
  258. }
  259. /** Creates a MenuItem that opens a submenu.
  260. Example:
  261. menu->addChild(createSubmenuItem("Edit", "",
  262. [=](Menu* menu) {
  263. menu->addChild(createMenuItem("Copy", "", [=]() {copy();}));
  264. menu->addChild(createMenuItem("Paste", "", [=]() {paste();}));
  265. }
  266. ));
  267. */
  268. template <class TMenuItem = ui::MenuItem>
  269. ui::MenuItem* createSubmenuItem(std::string text, std::string rightText, std::function<void(ui::Menu* menu)> createMenu, bool disabled = false) {
  270. struct Item : TMenuItem {
  271. std::function<void(ui::Menu* menu)> createMenu;
  272. ui::Menu* createChildMenu() override {
  273. ui::Menu* menu = new ui::Menu;
  274. createMenu(menu);
  275. return menu;
  276. }
  277. };
  278. Item* item = createMenuItem<Item>(text, rightText + (rightText.empty() ? "" : " ") + RIGHT_ARROW);
  279. item->createMenu = createMenu;
  280. item->disabled = disabled;
  281. return item;
  282. }
  283. /** Creates a MenuItem that when hovered, opens a submenu with several MenuItems indexed by an integer.
  284. Example:
  285. menu->addChild(createIndexSubmenuItem("Mode",
  286. {"Hi-fi", "Mid-fi", "Lo-fi"},
  287. [=]() {
  288. return module->getMode();
  289. },
  290. [=](int mode) {
  291. module->setMode(mode);
  292. }
  293. ));
  294. */
  295. template <class TMenuItem = ui::MenuItem>
  296. ui::MenuItem* createIndexSubmenuItem(std::string text, std::vector<std::string> labels, std::function<size_t()> getter, std::function<void(size_t val)> setter, bool disabled = false, bool alwaysConsume = false) {
  297. struct IndexItem : ui::MenuItem {
  298. std::function<size_t()> getter;
  299. std::function<void(size_t)> setter;
  300. size_t index;
  301. bool alwaysConsume;
  302. void step() override {
  303. size_t currIndex = getter();
  304. this->rightText = CHECKMARK(currIndex == index);
  305. MenuItem::step();
  306. }
  307. void onAction(const event::Action& e) override {
  308. setter(index);
  309. if (alwaysConsume)
  310. e.consume(this);
  311. }
  312. };
  313. struct Item : TMenuItem {
  314. std::function<size_t()> getter;
  315. std::function<void(size_t)> setter;
  316. std::vector<std::string> labels;
  317. bool alwaysConsume;
  318. void step() override {
  319. size_t currIndex = getter();
  320. std::string label = (currIndex < labels.size()) ? labels[currIndex] : "";
  321. this->rightText = label + " " + RIGHT_ARROW;
  322. TMenuItem::step();
  323. }
  324. ui::Menu* createChildMenu() override {
  325. ui::Menu* menu = new ui::Menu;
  326. for (size_t i = 0; i < labels.size(); i++) {
  327. IndexItem* item = createMenuItem<IndexItem>(labels[i]);
  328. item->getter = getter;
  329. item->setter = setter;
  330. item->index = i;
  331. item->alwaysConsume = alwaysConsume;
  332. menu->addChild(item);
  333. }
  334. return menu;
  335. }
  336. };
  337. Item* item = createMenuItem<Item>(text);
  338. item->getter = getter;
  339. item->setter = setter;
  340. item->labels = labels;
  341. item->disabled = disabled;
  342. item->alwaysConsume = alwaysConsume;
  343. return item;
  344. }
  345. /** Easy wrapper for createIndexSubmenuItem() that controls an integer index at a pointer address.
  346. Example:
  347. menu->addChild(createIndexPtrSubmenuItem("Mode",
  348. {"Hi-fi", "Mid-fi", "Lo-fi"},
  349. &module->mode
  350. ));
  351. */
  352. template <typename T>
  353. ui::MenuItem* createIndexPtrSubmenuItem(std::string text, std::vector<std::string> labels, T* ptr) {
  354. return createIndexSubmenuItem(text, labels,
  355. [=]() {return *ptr;},
  356. [=](size_t index) {*ptr = T(index);}
  357. );
  358. }
  359. } // namespace rack