From e8d0783abacfbde85d66519ac3608cf2209430eb Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 3 Mar 2018 17:51:27 -0500 Subject: [PATCH] Add manufacturer item to ModuleBrowser, other ModuleBrowser functionality --- include/ui.hpp | 6 + include/util/math.hpp | 21 +- src/app/ModuleBrowser.cpp | 389 ++++++++++++++++++++++++++++++++++++++ src/app/moduleBrowser.cpp | 212 --------------------- src/engine.cpp | 4 +- src/ui/Label.cpp | 12 +- src/ui/List.cpp | 2 +- src/ui/ScrollWidget.cpp | 15 +- 8 files changed, 435 insertions(+), 226 deletions(-) create mode 100644 src/app/ModuleBrowser.cpp delete mode 100644 src/app/moduleBrowser.cpp diff --git a/include/ui.hpp b/include/ui.hpp index 6f731e14..5ac91357 100644 --- a/include/ui.hpp +++ b/include/ui.hpp @@ -8,6 +8,12 @@ namespace rack { struct Label : Widget { std::string text; + enum Align { + LEFT_ALIGN, + CENTER_ALIGN, + RIGHT_ALIGN + }; + Align align = LEFT_ALIGN; Label() { box.size.y = BND_WIDGET_HEIGHT; } diff --git a/include/util/math.hpp b/include/util/math.hpp index a57ec3eb..b3a174f4 100644 --- a/include/util/math.hpp +++ b/include/util/math.hpp @@ -28,8 +28,8 @@ inline int max(int a, int b) { /** Limits a value between a minimum and maximum Assumes min <= max */ -inline int clamp(int x, int minimum, int maximum) { - return min(max(x, minimum), maximum); +inline int clamp(int x, int min, int max) { + return rack::min(rack::max(x, min), max); } /** Euclidean modulus, always returns 0 <= mod < base for positive base. @@ -74,11 +74,11 @@ inline bool isNear(float a, float b, float epsilon = 1.0e-6f) { /** Limits a value between a minimum and maximum Assumes min <= max */ -inline float clamp(float x, float minimum, float maximum) { - return fminf(fmaxf(x, minimum), maximum); +inline float clamp(float x, float min, float max) { + return fminf(fmaxf(x, min), max); } -/** Limits a value between a minimum and maximum +/** Limits a value between a min and max If min > max, switches the two values */ inline float clamp2(float x, float min, float max) { @@ -180,6 +180,7 @@ struct Vec { return isfinite(x) && isfinite(y); } Vec clamp(Rect bound); + Vec clamp2(Rect bound); }; @@ -260,8 +261,14 @@ struct Rect { inline Vec Vec::clamp(Rect bound) { return Vec( - clamp2(x, bound.pos.x, bound.pos.x + bound.size.x), - clamp2(y, bound.pos.y, bound.pos.y + bound.size.y)); + rack::clamp(x, bound.pos.x, bound.pos.x + bound.size.x), + rack::clamp(y, bound.pos.y, bound.pos.y + bound.size.y)); +} + +inline Vec Vec::clamp2(Rect bound) { + return Vec( + rack::clamp2(x, bound.pos.x, bound.pos.x + bound.size.x), + rack::clamp2(y, bound.pos.y, bound.pos.y + bound.size.y)); } diff --git a/src/app/ModuleBrowser.cpp b/src/app/ModuleBrowser.cpp new file mode 100644 index 00000000..ddcdfb89 --- /dev/null +++ b/src/app/ModuleBrowser.cpp @@ -0,0 +1,389 @@ +#include "app.hpp" +#include "plugin.hpp" +#include "window.hpp" +#include + + +#define BND_LABEL_FONT_SIZE 13 + + +namespace rack { + + +static std::set sFavoriteModels; + + +bool isMatch(std::string s, std::string search) { + s = lowercase(s); + search = lowercase(search); + return (s.find(search) != std::string::npos); +} + +static bool isModelMatch(Model *model, std::string search) { + if (search.empty()) + return true; + std::string s; + s += model->plugin->slug; + s += " "; + s += model->manufacturer; + s += " "; + s += model->name; + s += " "; + s += model->slug; + for (ModelTag tag : model->tags) { + s += " "; + s += gTagNames[tag]; + } + return isMatch(s, search); +} + + +struct FavoriteRadioButton : RadioButton { + Model *model = NULL; + + void onAction(EventAction &e) override; +}; + + +struct BrowserListItem : OpaqueWidget { + bool selected = false; + + BrowserListItem() { + box.size.y = 3 * BND_WIDGET_HEIGHT; + } + + void draw(NVGcontext *vg) override { + BNDwidgetState state = selected ? BND_HOVER : BND_DEFAULT; + bndMenuItem(vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, ""); + Widget::draw(vg); + } + + void onDragDrop(EventDragDrop &e) override { + if (e.origin != this) + return; + doAction(); + } + + void doAction() { + EventAction eAction; + eAction.consumed = true; + onAction(eAction); + if (eAction.consumed) { + // deletes `this` + gScene->setOverlay(NULL); + } + } + + void onMouseEnter(EventMouseEnter &e) override; +}; + + +struct ModelItem : BrowserListItem { + Model *model; + FavoriteRadioButton *favoriteButton; + Label *nameLabel; + Label *manufacturerLabel; + Label *tagsLabel; + + ModelItem() { + favoriteButton = new FavoriteRadioButton(); + favoriteButton->box.pos = Vec(7, BND_WIDGET_HEIGHT); + favoriteButton->box.size.x = 20; + favoriteButton->label = "★"; + addChild(favoriteButton); + + nameLabel = Widget::create