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.

281 lines
6.9KB

  1. #include "app/ModuleBrowser.hpp"
  2. #include "widget/OpaqueWidget.hpp"
  3. #include "widget/TransparentWidget.hpp"
  4. #include "widget/ZoomWidget.hpp"
  5. #include "ui/ScrollWidget.hpp"
  6. #include "ui/SequentialLayout.hpp"
  7. #include "ui/Label.hpp"
  8. #include "ui/TextField.hpp"
  9. #include "ui/MenuOverlay.hpp"
  10. #include "app/ModuleWidget.hpp"
  11. #include "app/Scene.hpp"
  12. #include "plugin.hpp"
  13. #include "app.hpp"
  14. #include "history.hpp"
  15. #include <set>
  16. #include <algorithm>
  17. namespace rack {
  18. namespace app {
  19. static std::set<Model*> sFavoriteModels;
  20. struct BrowserOverlay : widget::OpaqueWidget {
  21. void step() override {
  22. box = parent->box.zeroPos();
  23. widget::OpaqueWidget::step();
  24. }
  25. void onButton(const event::Button &e) override {
  26. widget::OpaqueWidget::onButton(e);
  27. if (e.getConsumed() != this)
  28. return;
  29. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
  30. this->visible = false;
  31. }
  32. }
  33. void onHoverKey(const event::HoverKey &e) override {
  34. if (e.action == GLFW_PRESS) {
  35. switch (e.key) {
  36. case GLFW_KEY_ESCAPE: {
  37. this->visible = false;
  38. e.consume(this);
  39. } break;
  40. }
  41. }
  42. if (!e.getConsumed())
  43. widget::OpaqueWidget::onHoverKey(e);
  44. }
  45. };
  46. struct ModuleBox : widget::OpaqueWidget {
  47. Model *model;
  48. /** Lazily created */
  49. widget::Widget *previewWidget = NULL;
  50. /** Number of frames since draw() has been called */
  51. int visibleFrames = 0;
  52. void setModel(Model *model) {
  53. this->model = model;
  54. box.size.x = 70.f;
  55. box.size.y = std::ceil(RACK_GRID_SIZE.y * 0.5f);
  56. math::Vec p;
  57. p.y = box.size.y;
  58. box.size.y += 40.0;
  59. ui::Label *nameLabel = new ui::Label;
  60. nameLabel->text = model->name;
  61. nameLabel->box.pos = p;
  62. p.y += nameLabel->box.size.y;
  63. addChild(nameLabel);
  64. ui::Label *pluginLabel = new ui::Label;
  65. pluginLabel->text = model->plugin->name;
  66. pluginLabel->box.pos = p;
  67. p.y += pluginLabel->box.size.y;
  68. addChild(pluginLabel);
  69. }
  70. void step() override {
  71. if (previewWidget && ++visibleFrames >= 60) {
  72. removeChild(previewWidget);
  73. delete previewWidget;
  74. previewWidget = NULL;
  75. }
  76. }
  77. void draw(const widget::DrawContext &ctx) override {
  78. visibleFrames = 0;
  79. // Lazily create ModuleWidget when drawn
  80. if (!previewWidget) {
  81. widget::Widget *transparentWidget = new widget::TransparentWidget;
  82. addChild(transparentWidget);
  83. widget::FramebufferWidget *fbWidget = new widget::FramebufferWidget;
  84. if (math::isNear(APP->window->pixelRatio, 1.0)) {
  85. // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
  86. fbWidget->oversample = 2.0;
  87. }
  88. transparentWidget->addChild(fbWidget);
  89. widget::ZoomWidget *zoomWidget = new widget::ZoomWidget;
  90. zoomWidget->setZoom(0.5f);
  91. fbWidget->addChild(zoomWidget);
  92. ModuleWidget *moduleWidget = model->createModuleWidgetNull();
  93. zoomWidget->addChild(moduleWidget);
  94. zoomWidget->box.size.x = moduleWidget->box.size.x * zoomWidget->zoom;
  95. zoomWidget->box.size.y = RACK_GRID_HEIGHT;
  96. float width = std::ceil(zoomWidget->box.size.x);
  97. box.size.x = std::max(box.size.x, width);
  98. previewWidget = transparentWidget;
  99. }
  100. widget::OpaqueWidget::draw(ctx);
  101. if (APP->event->hoveredWidget == this) {
  102. nvgBeginPath(ctx.vg);
  103. nvgRect(ctx.vg, 0.0, 0.0, box.size.x, box.size.y);
  104. nvgFillColor(ctx.vg, nvgRGBAf(1, 1, 1, 0.25));
  105. nvgFill(ctx.vg);
  106. }
  107. }
  108. void onButton(const event::Button &e) override;
  109. };
  110. struct BrowserSearchField : ui::TextField {
  111. };
  112. struct BrowserSidebar : widget::Widget {
  113. BrowserSearchField *searchField;
  114. BrowserSidebar() {
  115. searchField = new BrowserSearchField;
  116. addChild(searchField);
  117. }
  118. void step() override {
  119. searchField->box.size.x = box.size.x;
  120. widget::Widget::step();
  121. }
  122. };
  123. struct ModuleBrowser : widget::OpaqueWidget {
  124. BrowserSidebar *sidebar;
  125. ui::ScrollWidget *moduleScroll;
  126. ui::SequentialLayout *moduleLayout;
  127. ModuleBrowser() {
  128. sidebar = new BrowserSidebar;
  129. sidebar->box.size.x = 300;
  130. addChild(sidebar);
  131. moduleScroll = new ui::ScrollWidget;
  132. addChild(moduleScroll);
  133. moduleLayout = new ui::SequentialLayout;
  134. moduleLayout->spacing = math::Vec(10, 10);
  135. moduleScroll->container->addChild(moduleLayout);
  136. for (Plugin *plugin : plugin::plugins) {
  137. for (Model *model : plugin->models) {
  138. ModuleBox *moduleBox = new ModuleBox;
  139. moduleBox->setModel(model);
  140. moduleLayout->addChild(moduleBox);
  141. }
  142. }
  143. }
  144. void step() override {
  145. box = parent->box.zeroPos().grow(math::Vec(-50, -50));
  146. sidebar->box.size.y = box.size.y;
  147. moduleScroll->box.pos.x = sidebar->box.size.x;
  148. moduleScroll->box.size.x = box.size.x - sidebar->box.size.x;
  149. moduleScroll->box.size.y = box.size.y;
  150. moduleLayout->box.size.x = moduleScroll->box.size.x;
  151. moduleLayout->box.size.y = moduleLayout->getChildrenBoundingBox().getBottomRight().y;
  152. widget::OpaqueWidget::step();
  153. }
  154. void draw(const widget::DrawContext &ctx) override {
  155. bndMenuBackground(ctx.vg, 0.0, 0.0, box.size.x, box.size.y, 0);
  156. widget::Widget::draw(ctx);
  157. }
  158. };
  159. // Implementations to resolve dependencies
  160. void ModuleBox::onButton(const event::Button &e) {
  161. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
  162. // Create module
  163. ModuleWidget *moduleWidget = model->createModuleWidget();
  164. assert(moduleWidget);
  165. APP->scene->rackWidget->addModuleAtMouse(moduleWidget);
  166. // This is a bit nonstandard/unsupported usage, but pretend the moduleWidget was clicked so it can be dragged in the RackWidget
  167. // e.consume(moduleWidget);
  168. // Close Module Browser
  169. BrowserOverlay *overlay = getAncestorOfType<BrowserOverlay>();
  170. overlay->visible = false;
  171. // Push ModuleAdd history action
  172. history::ModuleAdd *h = new history::ModuleAdd;
  173. h->setModule(moduleWidget);
  174. APP->history->push(h);
  175. }
  176. widget::OpaqueWidget::onButton(e);
  177. }
  178. // Global functions
  179. widget::Widget *moduleBrowserCreate() {
  180. BrowserOverlay *overlay = new BrowserOverlay;
  181. ModuleBrowser *browser = new ModuleBrowser;
  182. overlay->addChild(browser);
  183. return overlay;
  184. }
  185. json_t *moduleBrowserToJson() {
  186. json_t *rootJ = json_object();
  187. json_t *favoritesJ = json_array();
  188. for (Model *model : sFavoriteModels) {
  189. json_t *modelJ = json_object();
  190. json_object_set_new(modelJ, "plugin", json_string(model->plugin->slug.c_str()));
  191. json_object_set_new(modelJ, "model", json_string(model->slug.c_str()));
  192. json_array_append_new(favoritesJ, modelJ);
  193. }
  194. json_object_set_new(rootJ, "favorites", favoritesJ);
  195. return rootJ;
  196. }
  197. void moduleBrowserFromJson(json_t *rootJ) {
  198. json_t *favoritesJ = json_object_get(rootJ, "favorites");
  199. if (favoritesJ) {
  200. size_t i;
  201. json_t *favoriteJ;
  202. json_array_foreach(favoritesJ, i, favoriteJ) {
  203. json_t *pluginJ = json_object_get(favoriteJ, "plugin");
  204. json_t *modelJ = json_object_get(favoriteJ, "model");
  205. if (!pluginJ || !modelJ)
  206. continue;
  207. std::string pluginSlug = json_string_value(pluginJ);
  208. std::string modelSlug = json_string_value(modelJ);
  209. Model *model = plugin::getModel(pluginSlug, modelSlug);
  210. if (!model)
  211. continue;
  212. sFavoriteModels.insert(model);
  213. }
  214. }
  215. }
  216. } // namespace app
  217. } // namespace rack