Browse Source

Implement all sorting options to module browser.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
1927def7e1
3 changed files with 114 additions and 81 deletions
  1. +3
    -1
      include/settings.hpp
  2. +100
    -80
      src/app/ModuleBrowser.cpp
  3. +11
    -0
      src/settings.cpp

+ 3
- 1
include/settings.hpp View File

@@ -66,8 +66,8 @@ extern bool showTipsOnLaunch;
extern int tipIndex;
enum ModuleBrowserSort {
MODULE_BROWSER_SORT_UPDATED,
MODULE_BROWSER_SORT_MOST_USED,
MODULE_BROWSER_SORT_LAST_USED,
MODULE_BROWSER_SORT_MOST_USED,
MODULE_BROWSER_SORT_BRAND,
MODULE_BROWSER_SORT_NAME,
MODULE_BROWSER_SORT_RANDOM,
@@ -81,7 +81,9 @@ struct ModuleUsage {
int count = 0;
double lastTime = NAN;
};
// pluginSlug, moduleSlug -> ModuleUsage
extern std::map<std::string, std::map<std::string, ModuleUsage>> moduleUsages;
ModuleUsage* getModuleUsage(const std::string& pluginSlug, const std::string& moduleSlug);

json_t* toJson();
void fromJson(json_t* rootJ);


+ 100
- 80
src/app/ModuleBrowser.cpp View File

@@ -381,8 +381,8 @@ struct TagButton : ui::ChoiceButton {

static const std::string sortNames[] = {
"Last updated",
"Most used",
"Last used",
"Most used",
"Brand",
"Module name",
"Random",
@@ -446,7 +446,7 @@ struct ZoomButton : ui::ChoiceButton {

for (float zoom = 0.f; zoom >= -2.f; zoom -= 0.5f) {
ZoomItem* sortItem = new ZoomItem;
sortItem->text = string::f("%.3g%%", std::pow(2.f, zoom) * 100.f);
sortItem->text = string::f("%.2g%%", std::pow(2.f, zoom) * 100.f);
sortItem->zoom = zoom;
sortItem->browser = browser;
menu->addChild(sortItem);
@@ -455,7 +455,7 @@ struct ZoomButton : ui::ChoiceButton {

void step() override {
text = "Zoom: ";
text += string::f("%.3g%%", std::pow(2.f, settings::moduleBrowserZoom) * 100.f);
text += string::f("%.2g%%", std::pow(2.f, settings::moduleBrowserZoom) * 100.f);
ChoiceButton::step();
}
};
@@ -487,6 +487,8 @@ struct ModuleBrowser : widget::OpaqueWidget {
std::string brand;
std::set<int> tagIds = {};

std::map<plugin::Model*, float> prefilteredModelScores;

ModuleBrowser() {
float margin = 10;

@@ -560,7 +562,7 @@ struct ModuleBrowser : widget::OpaqueWidget {
void resetModelBoxes() {
modelContainer->clearChildren();
// Iterate plugins
for (int i = 0; i < 100; i++)
// for (int i = 0; i < 100; i++)
for (plugin::Plugin* plugin : plugin::plugins) {
// Get module slugs from module whitelist
const auto& pluginIt = settings::moduleWhitelist.find(plugin->slug);
@@ -582,6 +584,8 @@ struct ModuleBrowser : widget::OpaqueWidget {
}

void updateZoom() {
modelScroll->offset = math::Vec();

for (Widget* w : modelContainer->children) {
ModelBox* mb = reinterpret_cast<ModelBox*>(w);
assert(mb);
@@ -607,54 +611,106 @@ struct ModuleBrowser : widget::OpaqueWidget {
Widget::draw(args);
}

bool isModelVisible(plugin::Model* model, const std::string& brand, std::set<int> tagIds) {
// Filter brand
if (!brand.empty()) {
if (model->plugin->brand != brand)
return false;
}

// Filter tag
for (int tagId : tagIds) {
auto it = std::find(model->tags.begin(), model->tags.end(), tagId);
if (it == model->tags.end())
return false;
}

return true;
};

// Determines if there is at least 1 visible Model with a given brand and tag
bool hasVisibleModel(const std::string& brand, std::set<int> tagIds) {
for (const auto& pair : prefilteredModelScores) {
plugin::Model* model = pair.first;
if (isModelVisible(model, brand, tagIds))
return true;
}
return false;
};

template <typename F>
void sortModels(F f) {
modelContainer->children.sort([&](Widget* w1, Widget* w2) {
ModelBox* m1 = reinterpret_cast<ModelBox*>(w1);
ModelBox* m2 = reinterpret_cast<ModelBox*>(w2);
return f(m1) < f(m2);
});
}

void refresh() {
// Reset scroll position
modelScroll->offset = math::Vec();

auto isModelVisible = [&](plugin::Model* model, const std::string& brand, std::set<int> tagIds) -> bool {
// Filter brand
if (brand != "") {
if (model->plugin->brand != brand)
return false;
}

// Filter tag
for (int tagId : tagIds) {
auto it = std::find(model->tags.begin(), model->tags.end(), tagId);
if (it == model->tags.end())
return false;
}

return true;
};
prefilteredModelScores.clear();

// Filter ModelBoxes by brand and tag
for (Widget* w : modelContainer->children) {
ModelBox* m = dynamic_cast<ModelBox*>(w);
assert(m);
m->visible = isModelVisible(m->model, brand, tagIds);
ModelBox* m = reinterpret_cast<ModelBox*>(w);
m->setVisible(isModelVisible(m->model, brand, tagIds));
}

std::map<plugin::Model*, float> prefilteredModelScores;

// Filter and sort by search results
if (search.empty()) {
// Add all models to prefilteredModelScores with scores of 1
for (Widget* w : modelContainer->children) {
ModelBox* m = dynamic_cast<ModelBox*>(w);
assert(m);
ModelBox* m = reinterpret_cast<ModelBox*>(w);
prefilteredModelScores[m->model] = 1.f;
}

// Sort ModelBoxes
modelContainer->children.sort([&](Widget * w1, Widget * w2) {
ModelBox* m1 = dynamic_cast<ModelBox*>(w1);
ModelBox* m2 = dynamic_cast<ModelBox*>(w2);
// Sort by (modifiedTimestamp descending, plugin brand)
auto t1 = std::make_tuple(-m1->model->plugin->modifiedTimestamp, m1->model->plugin->brand);
auto t2 = std::make_tuple(-m2->model->plugin->modifiedTimestamp, m2->model->plugin->brand);
return t1 < t2;
});
if (settings::moduleBrowserSort == settings::MODULE_BROWSER_SORT_UPDATED) {
sortModels([](ModelBox* m) {
plugin::Plugin* p = m->model->plugin;
return std::make_tuple(-p->modifiedTimestamp, p->brand, m->model->name);
});
}
else if (settings::moduleBrowserSort == settings::MODULE_BROWSER_SORT_LAST_USED) {
sortModels([](ModelBox* m) {
plugin::Plugin* p = m->model->plugin;
const settings::ModuleUsage* mu = settings::getModuleUsage(p->slug, m->model->slug);
double lastTime = mu ? mu->lastTime : -INFINITY;
return std::make_tuple(-lastTime, -p->modifiedTimestamp, p->brand);
});
}
else if (settings::moduleBrowserSort == settings::MODULE_BROWSER_SORT_MOST_USED) {
sortModels([](ModelBox* m) {
plugin::Plugin* p = m->model->plugin;
const settings::ModuleUsage* mu = settings::getModuleUsage(p->slug, m->model->slug);
int count = mu ? mu->count : 0;
double lastTime = mu ? mu->lastTime : -INFINITY;
return std::make_tuple(-count, -lastTime, -p->modifiedTimestamp, p->brand);
});
}
else if (settings::moduleBrowserSort == settings::MODULE_BROWSER_SORT_BRAND) {
sortModels([](ModelBox* m) {
return std::make_tuple(m->model->plugin->brand, m->model->name);
});
}
else if (settings::moduleBrowserSort == settings::MODULE_BROWSER_SORT_NAME) {
sortModels([](ModelBox* m) {
return std::make_tuple(m->model->name, m->model->plugin->brand);
});
}
else if (settings::moduleBrowserSort == settings::MODULE_BROWSER_SORT_RANDOM) {
std::map<ModelBox*, uint64_t> randomOrder;
for (Widget* w : modelContainer->children) {
ModelBox* m = reinterpret_cast<ModelBox*>(w);
randomOrder[m] = random::u64();
}
sortModels([&](ModelBox* m) {
return get(randomOrder, m, 0);
});
}
}
else {
// Lazily initialize search database
@@ -667,15 +723,12 @@ struct ModuleBrowser : widget::OpaqueWidget {
// DEBUG("%s %s\t\t%f", result._key->plugin->slug.c_str(), result._key->slug.c_str(), result._score);
}
// Sort by score
modelContainer->children.sort([&](Widget *w1, Widget *w2) {
ModelBox* m1 = dynamic_cast<ModelBox*>(w1);
ModelBox* m2 = dynamic_cast<ModelBox*>(w2);
// If score was not computed, the ModelBox will not visible so the order doesn't matter.
return get(prefilteredModelScores, m1->model, 0.f) > get(prefilteredModelScores, m2->model, 0.f);
sortModels([&](ModelBox* m) {
return get(prefilteredModelScores, m->model, 0.f);
});
// Filter by whether the score is above the threshold
for (Widget* w : modelContainer->children) {
ModelBox* m = dynamic_cast<ModelBox*>(w);
ModelBox* m = reinterpret_cast<ModelBox*>(w);
assert(m);
if (m->visible) {
if (prefilteredModelScores.find(m->model) == prefilteredModelScores.end())
@@ -683,44 +736,6 @@ struct ModuleBrowser : widget::OpaqueWidget {
}
}
}

// Determines if there is at least 1 visible Model with a given brand and tag
auto hasVisibleModel = [&](const std::string& brand, std::set<int> tagIds) -> bool {
for (auto& pair : prefilteredModelScores) {
plugin::Model* model = pair.first;
if (isModelVisible(model, brand, tagIds))
return true;
}
return false;
};

// // Enable brand and tag items that are available in visible ModelBoxes
// int brandsLen = 0;
// for (Widget* w : sidebar->brandList->children) {
// BrandItem* item = dynamic_cast<BrandItem*>(w);
// assert(item);
// item->disabled = !hasVisibleModel(item->text, tagIds);
// if (!item->disabled)
// brandsLen++;
// }
// sidebar->brandLabel->text = string::f("Brands (%d)", brandsLen);

// int tagsLen = 0;
// for (Widget* w : sidebar->tagList->children) {
// TagItem* item = dynamic_cast<TagItem*>(w);
// assert(item);
// item->disabled = !hasVisibleModel(brand, {item->tagId});
// if (!item->disabled)
// tagsLen++;
// }
// sidebar->tagLabel->text = string::f("Tags (%d)", tagsLen);

// // Count models
// int modelsLen = 0;
// for (Widget* w : modelContainer->children) {
// if (w->visible)
// modelsLen++;
// }
}

void clear() {
@@ -730,6 +745,11 @@ struct ModuleBrowser : widget::OpaqueWidget {
tagIds = {};
refresh();
}

void onShow(const event::Show& e) override {
refresh();
OpaqueWidget::onShow(e);
}
};


@@ -770,7 +790,7 @@ inline void BrowserSearchField::onAction(const event::Action& e) {
ModelBox* mb = NULL;
for (Widget* w : browser->modelContainer->children) {
if (w->isVisible()) {
mb = dynamic_cast<ModelBox*>(w);
mb = reinterpret_cast<ModelBox*>(w);
break;
}
}


+ 11
- 0
src/settings.cpp View File

@@ -58,6 +58,17 @@ std::map<std::string, std::set<std::string>> moduleWhitelist = {};
std::map<std::string, std::map<std::string, ModuleUsage>> moduleUsages = {};


ModuleUsage* getModuleUsage(const std::string& pluginSlug, const std::string& moduleSlug) {
auto it1 = moduleUsages.find(pluginSlug);
if (it1 == moduleUsages.end())
return NULL;
auto it2 = it1->second.find(moduleSlug);
if (it2 == it1->second.end())
return NULL;
return &it2->second;
}


json_t* toJson() {
json_t* rootJ = json_object();



Loading…
Cancel
Save