Browse Source

Add search functionality to ModuleBrowser

tags/v1.0.0
Andrew Belt 5 years ago
parent
commit
55ad83d890
3 changed files with 73 additions and 24 deletions
  1. +2
    -2
      include/string.hpp
  2. +63
    -16
      src/app/ModuleBrowser.cpp
  3. +8
    -6
      src/string.cpp

+ 2
- 2
include/string.hpp View File

@@ -15,9 +15,9 @@ Remember that "%s" must reference a `char *`, so use `.c_str()` for `std::string
*/
std::string f(const char *format, ...);
/** Replaces all characters to lowercase letters */
std::string lowercase(std::string s);
std::string lowercase(const std::string &s);
/** Replaces all characters to uppercase letters */
std::string uppercase(std::string s);
std::string uppercase(const std::string &s);
/** Truncates and adds "..." to a string, not exceeding `len` characters */
std::string ellipsize(const std::string &s, size_t len);
bool startsWith(const std::string &str, const std::string &prefix);


+ 63
- 16
src/app/ModuleBrowser.cpp View File

@@ -11,6 +11,8 @@
#include "app/Scene.hpp"
#include "plugin.hpp"
#include "app.hpp"
#include "plugin/Model.hpp"
#include "string.hpp"
#include "history.hpp"

#include <set>
@@ -24,6 +26,35 @@ namespace app {
static std::set<plugin::Model*> sFavoriteModels;



bool isMatch(const std::string &s, const std::string &search) {
std::string s2 = string::lowercase(s);
std::string search2 = string::lowercase(search);
return (s2.find(search2) != std::string::npos);
}

static bool isModelMatch(plugin::Model *model, const std::string &search) {
if (search.empty())
return true;
std::string s;
s += model->plugin->slug;
s += " ";
s += model->plugin->author;
s += " ";
s += model->plugin->name;
s += " ";
s += model->slug;
s += " ";
s += model->name;
for (const std::string &tag : model->tags) {
// TODO Normalize tag
s += tag;
s += " ";
}
return isMatch(s, search);
}


struct BrowserOverlay : widget::OpaqueWidget {
void step() override {
box = parent->box.zeroPos();
@@ -58,7 +89,7 @@ struct BrowserOverlay : widget::OpaqueWidget {
};


struct ModuleBox : widget::OpaqueWidget {
struct ModelBox : widget::OpaqueWidget {
plugin::Model *model;
/** Lazily created */
widget::Widget *previewWidget = NULL;
@@ -150,6 +181,7 @@ struct ModuleBox : widget::OpaqueWidget {


struct BrowserSearchField : ui::TextField {
void onChange(const event::Change &e) override;
};


@@ -170,26 +202,26 @@ struct BrowserSidebar : widget::Widget {

struct ModuleBrowser : widget::OpaqueWidget {
BrowserSidebar *sidebar;
ui::ScrollWidget *moduleScroll;
ui::SequentialLayout *moduleLayout;
ui::ScrollWidget *modelScroll;
ui::SequentialLayout *modelContainer;

ModuleBrowser() {
sidebar = new BrowserSidebar;
sidebar->box.size.x = 300;
addChild(sidebar);

moduleScroll = new ui::ScrollWidget;
addChild(moduleScroll);
modelScroll = new ui::ScrollWidget;
addChild(modelScroll);

moduleLayout = new ui::SequentialLayout;
moduleLayout->spacing = math::Vec(10, 10);
moduleScroll->container->addChild(moduleLayout);
modelContainer = new ui::SequentialLayout;
modelContainer->spacing = math::Vec(10, 10);
modelScroll->container->addChild(modelContainer);

for (plugin::Plugin *plugin : plugin::plugins) {
for (plugin::Model *model : plugin->models) {
ModuleBox *moduleBox = new ModuleBox;
ModelBox *moduleBox = new ModelBox;
moduleBox->setModel(model);
moduleLayout->addChild(moduleBox);
modelContainer->addChild(moduleBox);
}
}
}
@@ -199,11 +231,11 @@ struct ModuleBrowser : widget::OpaqueWidget {

sidebar->box.size.y = box.size.y;

moduleScroll->box.pos.x = sidebar->box.size.x;
moduleScroll->box.size.x = box.size.x - sidebar->box.size.x;
moduleScroll->box.size.y = box.size.y;
moduleLayout->box.size.x = moduleScroll->box.size.x;
moduleLayout->box.size.y = moduleLayout->getChildrenBoundingBox().getBottomRight().y;
modelScroll->box.pos.x = sidebar->box.size.x;
modelScroll->box.size.x = box.size.x - sidebar->box.size.x;
modelScroll->box.size.y = box.size.y;
modelContainer->box.size.x = modelScroll->box.size.x;
modelContainer->box.size.y = modelContainer->getChildrenBoundingBox().getBottomRight().y;

widget::OpaqueWidget::step();
}
@@ -212,13 +244,22 @@ struct ModuleBrowser : widget::OpaqueWidget {
bndMenuBackground(ctx.vg, 0.0, 0.0, box.size.x, box.size.y, 0);
widget::Widget::draw(ctx);
}

void setSearch(const std::string &search) {
for (Widget *w : modelContainer->children) {
ModelBox *modelBox = dynamic_cast<ModelBox*>(w);
assert(modelBox);
bool match = isModelMatch(modelBox->model, search);
modelBox->visible = match;
}
}
};


// Implementations to resolve dependencies


void ModuleBox::onButton(const event::Button &e) {
void ModelBox::onButton(const event::Button &e) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
// Create module
ModuleWidget *moduleWidget = model->createModuleWidget();
@@ -241,6 +282,12 @@ void ModuleBox::onButton(const event::Button &e) {
widget::OpaqueWidget::onButton(e);
}

void BrowserSearchField::onChange(const event::Change &e) {
ModuleBrowser *browser = getAncestorOfType<ModuleBrowser>();
browser->setSearch(text);
}



// Global functions



+ 8
- 6
src/string.cpp View File

@@ -24,14 +24,16 @@ std::string f(const char *format, ...) {
return s;
}

std::string lowercase(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); });
return s;
std::string lowercase(const std::string &s) {
std::string r = s;
std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c){ return std::tolower(c); });
return r;
}

std::string uppercase(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::toupper(c); });
return s;
std::string uppercase(const std::string &s) {
std::string r = s;
std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c){ return std::toupper(c); });
return r;
}

std::string ellipsize(const std::string &s, size_t len) {


Loading…
Cancel
Save