filter mods to download by platform

This commit is contained in:
HJfod 2022-09-01 23:36:26 +03:00
parent 9eee266e86
commit 59ada9e7f0
9 changed files with 158 additions and 62 deletions

View file

@ -31,9 +31,13 @@ namespace geode {
operator int() const { return m_value; }
template<class T>
static Type cast(T t) {
static PlatformID from(T t) {
return static_cast<Type>(t);
}
template<class T>
T to() const {
return static_cast<T>(m_value);
}
static constexpr const char* toString(Type lp) {
switch (lp) {

View file

@ -383,12 +383,25 @@ std::vector<IndexItem> const& Index::getItems() const {
return m_items;
}
std::vector<IndexItem> Index::getUninstalledItems() const {
std::vector<IndexItem> Index::getNoninstalledItems(
std::optional<std::unordered_set<PlatformID>> const& platforms
) const {
std::vector<IndexItem> items;
for (auto& item : m_items) {
if (!Loader::get()->isModInstalled(item.m_info.m_id)) {
if (item.m_download.m_platforms.count(GEODE_PLATFORM_TARGET)) {
items.push_back(item);
// return whatever is available on requested platforms
if (platforms) {
for (auto& plat : platforms.value()) {
if (item.m_download.m_platforms.count(plat)) {
items.push_back(item);
}
}
}
// otherwise just return whatever is available on current platform
else {
if (item.m_download.m_platforms.count(GEODE_PLATFORM_TARGET)) {
items.push_back(item);
}
}
}
}

View file

@ -2,6 +2,7 @@
#include <Geode/Geode.hpp>
#include <mutex>
#include <optional>
USE_GEODE_NAMESPACE();
@ -132,7 +133,9 @@ public:
static Index* get();
std::vector<IndexItem> const& getItems() const;
std::vector<IndexItem> getUninstalledItems() const;
std::vector<IndexItem> getNoninstalledItems(
std::optional<std::unordered_set<PlatformID>> const& platforms
) const;
bool isKnownItem(std::string const& id) const;
IndexItem getKnownItem(std::string const& id) const;
Result<InstallTicket*> installItems(

View file

@ -233,8 +233,12 @@ void ModListLayer::reloadList() {
}
// create new list
const char* filter = m_searchInput ? m_searchInput->getString() : nullptr;
auto list = ModListView::create(g_tab, 358.f, 190.f, filter, m_searchFlags);
m_query.m_searchFilter =
m_searchInput &&
m_searchInput->getString() &&
strlen(m_searchInput->getString()) ?
std::optional<std::string>(m_searchInput->getString()) : std::nullopt;
auto list = ModListView::create(g_tab, 358.f, 190.f, m_query);
list->setLayer(this);
// set list status
@ -299,7 +303,7 @@ void ModListLayer::reloadList() {
// check if the user has searched something,
// and show visual indicator if so
auto hasQuery = filter && strlen(filter);
auto hasQuery = m_query.m_searchFilter.has_value();
m_searchBtn->setVisible(!hasQuery);
m_searchClearBtn->setVisible(hasQuery);

View file

@ -24,7 +24,7 @@ protected:
CCNode* m_searchBG = nullptr;
CCTextInputNode* m_searchInput = nullptr;
LoadingCircle* m_loadingCircle = nullptr;
int m_searchFlags = ModListView::s_allFlags;
ModListQuery m_query;
virtual ~ModListLayer();

View file

@ -367,20 +367,24 @@ void ModListView::loadCell(TableViewCell* cell, unsigned int index) {
}
}
bool ModListView::filter(ModInfo const& info, const char* searchFilter, int searchFlags) {
if (!searchFilter || !strlen(searchFilter)) return true;
bool ModListView::filter(
ModInfo const& info,
std::optional<std::string> const& searchFilter,
int searchFlags
) {
if (!searchFilter) return true;
auto check = [searchFlags, searchFilter](SearchFlags flag, std::string const& name) -> bool {
if (!(searchFlags & flag)) return false;
return string_utils::contains(
string_utils::toLower(name),
string_utils::toLower(searchFilter)
string_utils::toLower(searchFilter.value())
);
};
if (check(SearchFlags::Name, info.m_name)) return true;
if (check(SearchFlags::ID, info.m_id)) return true;
if (check(SearchFlags::Developer, info.m_developer)) return true;
if (check(SearchFlags::Description, info.m_description)) return true;
if (check(SearchFlags::Details, info.m_details)) return true;
if (check(SearchFlag::Name, info.m_name)) return true;
if (check(SearchFlag::ID, info.m_id)) return true;
if (check(SearchFlag::Developer, info.m_developer)) return true;
if (check(SearchFlag::Description, info.m_description)) return true;
if (check(SearchFlag::Details, info.m_details)) return true;
return false;
}
@ -416,8 +420,7 @@ bool ModListView::init(
ModListType type,
float width,
float height,
const char* searchFilter,
int searchFlags
ModListQuery query
) {
if (!mods) {
switch (type) {
@ -429,7 +432,7 @@ bool ModListView::init(
}
// internal geode representation always at the top
auto imod = Loader::getInternalMod();
if (this->filter(imod->getModInfo(), searchFilter, searchFlags)) {
if (this->filter(imod->getModInfo(), query.m_searchFilter, query.m_searchFlags)) {
mods->addObject(new ModObject(imod));
}
// then other mods
@ -438,7 +441,7 @@ bool ModListView::init(
// loaded, it's as good as not existing
// (because it doesn't)
if (mod->isUninstalled() && !mod->isLoaded()) continue;
if (this->filter(mod->getModInfo(), searchFilter, searchFlags)) {
if (this->filter(mod->getModInfo(), query.m_searchFilter, query.m_searchFlags)) {
mods->addObject(new ModObject(mod));
}
}
@ -449,8 +452,10 @@ bool ModListView::init(
case ModListType::Download: {
mods = CCArray::create();
for (auto const& item : Index::get()->getUninstalledItems()) {
mods->addObject(new ModObject(item));
for (auto const& item : Index::get()->getNoninstalledItems(query.m_platforms)) {
if (this->filter(item.m_info, query.m_searchFilter, query.m_searchFlags)) {
mods->addObject(new ModObject(item));
}
}
if (!mods->count()) {
m_status = Status::NoModsFound;
@ -473,12 +478,11 @@ ModListView* ModListView::create(
ModListType type,
float width,
float height,
const char* searchFilter,
int searchFlags
ModListQuery query
) {
auto pRet = new ModListView;
if (pRet) {
if (pRet->init(mods, type, width, height, searchFilter, searchFlags)) {
if (pRet->init(mods, type, width, height, query)) {
pRet->autorelease();
return pRet;
}
@ -491,10 +495,9 @@ ModListView* ModListView::create(
ModListType type,
float width,
float height,
const char* searchFilter,
int searchFlags
ModListQuery query
) {
return ModListView::create(nullptr, type, width, height, searchFilter, searchFlags);
return ModListView::create(nullptr, type, width, height, query);
}
ModListView::Status ModListView::getStatus() const {

View file

@ -2,9 +2,12 @@
#include <Geode/Geode.hpp>
#include <Index.hpp>
#include <optional>
USE_GEODE_NAMESPACE();
struct ModListQuery;
enum class ModListType {
Installed,
Download,
@ -74,10 +77,8 @@ public:
static ModCell* create(ModListView* list, const char* key, CCSize size);
};
class ModListView : public CustomListView {
public:
// this is not enum class so | works
enum SearchFlags {
struct SearchFlag {
enum : int {
Name = 0b1,
ID = 0b10,
Developer = 0b100,
@ -85,14 +86,24 @@ public:
Description = 0b10000,
Details = 0b100000,
};
static constexpr int s_allFlags =
SearchFlags::Name |
SearchFlags::ID |
SearchFlags::Developer |
SearchFlags::Credits |
SearchFlags::Description |
SearchFlags::Details;
};
using SearchFlags = int;
static constexpr SearchFlags ALL_FLAGS =
SearchFlag::Name |
SearchFlag::ID |
SearchFlag::Developer |
SearchFlag::Credits |
SearchFlag::Description |
SearchFlag::Details;
struct ModListQuery {
std::optional<std::string> m_searchFilter = std::nullopt;
int m_searchFlags = ALL_FLAGS;
std::unordered_set<PlatformID> m_platforms { GEODE_PLATFORM_TARGET };
};
class ModListView : public CustomListView {
protected:
enum class Status {
OK,
@ -113,10 +124,13 @@ protected:
ModListType type,
float width,
float height,
const char* searchFilter,
int searchFlags
ModListQuery query
);
bool filter(
ModInfo const& info,
std::optional<std::string> const& searchFilter,
SearchFlags searchFlags
);
bool filter(ModInfo const& info, const char* searchFilter, int searchFlags);
public:
static ModListView* create(
@ -124,15 +138,13 @@ public:
ModListType type = ModListType::Installed,
float width = 358.f,
float height = 220.f,
const char* searchFilter = nullptr,
int searchFlags = 0
ModListQuery query = ModListQuery()
);
static ModListView* create(
ModListType type,
float width = 358.f,
float height = 220.f,
const char* searchFilter = nullptr,
int searchFlags = 0
ModListQuery query = ModListQuery()
);
void updateAllStates(ModCell* toggled = nullptr);

View file

@ -12,7 +12,7 @@ bool SearchFilterPopup::setup(ModListLayer* layer, ModListType type) {
auto winSize = CCDirector::sharedDirector()->getWinSize();
auto pos = CCPoint { winSize.width / 2 - 145.f, winSize.height / 2 + 35.f };
auto matchTitle = CCLabelBMFont::create("Search fields", "goldFont.fnt");
auto matchTitle = CCLabelBMFont::create("Match fields", "goldFont.fnt");
matchTitle->setPosition(winSize.width / 2 - 90.f, winSize.height / 2 + 65.f);
matchTitle->setScale(.5f);
m_mainLayer->addChild(matchTitle);
@ -27,13 +27,12 @@ bool SearchFilterPopup::setup(ModListLayer* layer, ModListType type) {
matchBG->setScale(.5f);
m_mainLayer->addChild(matchBG);
this->addSearchMatch("Name", ModListView::SearchFlags::Name, pos);
this->addSearchMatch("ID", ModListView::SearchFlags::ID, pos);
this->addSearchMatch("Credits", ModListView::SearchFlags::Credits, pos);
this->addSearchMatch("Description", ModListView::SearchFlags::Description, pos);
this->addSearchMatch("Details", ModListView::SearchFlags::Details, pos);
this->addSearchMatch("Developer", ModListView::SearchFlags::Developer, pos);
this->addSearchMatch("Name", SearchFlag::Name, pos);
this->addSearchMatch("ID", SearchFlag::ID, pos);
this->addSearchMatch("Credits", SearchFlag::Credits, pos);
this->addSearchMatch("Description", SearchFlag::Description, pos);
this->addSearchMatch("Details", SearchFlag::Details, pos);
this->addSearchMatch("Developer", SearchFlag::Developer, pos);
auto line = CCSprite::createWithSpriteFrameName("edit_vLine_001.png");
line->setPosition({ winSize.width / 2, winSize.height / 2 - 20.f });
@ -41,13 +40,35 @@ bool SearchFilterPopup::setup(ModListLayer* layer, ModListType type) {
line->setOpacity(100);
m_mainLayer->addChild(line);
auto platformTitle = CCLabelBMFont::create("Platforms", "goldFont.fnt");
platformTitle->setPosition(winSize.width / 2 + 90.f, winSize.height / 2 + 65.f);
platformTitle->setScale(.5f);
m_mainLayer->addChild(platformTitle);
auto platformBG = CCScale9Sprite::create(
"square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f }
);
platformBG->setColor({ 0, 0, 0 });
platformBG->setOpacity(90);
platformBG->setContentSize({ 290.f, 300.f });
platformBG->setPosition(winSize.width / 2 + 90.f, winSize.height / 2 - 21.f);
platformBG->setScale(.5f);
m_mainLayer->addChild(platformBG);
pos = CCPoint { winSize.width / 2 + 45.f, winSize.height / 2 + 35.f };
this->addPlatformToggle("Windows", PlatformID::Windows, pos);
this->addPlatformToggle("MacOS", PlatformID::MacOS, pos);
this->addPlatformToggle("iOS", PlatformID::iOS, pos);
this->addPlatformToggle("Android", PlatformID::Android, pos);
return true;
}
void SearchFilterPopup::addSearchMatch(const char* title, int flag, CCPoint& pos) {
GameToolbox::createToggleButton(
title, menu_selector(SearchFilterPopup::onToggle),
m_modLayer->m_searchFlags & flag,
title, menu_selector(SearchFilterPopup::onSearchToggle),
m_modLayer->m_query.m_searchFlags & flag,
m_buttonMenu, pos, this,
m_buttonMenu, .5f, .5f, 100.f,
{ 10.f, .0f }, nullptr, false, flag, nullptr
@ -55,14 +76,46 @@ void SearchFilterPopup::addSearchMatch(const char* title, int flag, CCPoint& pos
pos.y -= 22.5f;
}
void SearchFilterPopup::onToggle(cocos2d::CCObject* pSender) {
if (as<CCMenuItemToggler*>(pSender)->isToggled()) {
m_modLayer->m_searchFlags &= ~pSender->getTag();
void SearchFilterPopup::addPlatformToggle(
const char* title,
PlatformID id,
CCPoint& pos
) {
GameToolbox::createToggleButton(
title, menu_selector(SearchFilterPopup::onPlatformToggle),
m_modLayer->m_query.m_platforms.count(id),
m_buttonMenu, pos, this,
m_buttonMenu, .5f, .5f, 100.f,
{ 10.f, .0f }, nullptr, false, id.to<int>(), nullptr
)->setTag(id.to<int>());
pos.y -= 22.5f;
}
void SearchFilterPopup::onSearchToggle(CCObject* sender) {
if (as<CCMenuItemToggler*>(sender)->isToggled()) {
m_modLayer->m_query.m_searchFlags &= ~sender->getTag();
} else {
m_modLayer->m_searchFlags |= pSender->getTag();
m_modLayer->m_query.m_searchFlags |= sender->getTag();
}
}
void SearchFilterPopup::onPlatformToggle(CCObject* sender) {
if (as<CCMenuItemToggler*>(sender)->isToggled()) {
m_modLayer->m_query.m_platforms.erase(
PlatformID::from(sender->getTag())
);
} else {
m_modLayer->m_query.m_platforms.insert(
PlatformID::from(sender->getTag())
);
}
}
void SearchFilterPopup::onClose(CCObject* sender) {
Popup::onClose(sender);
m_modLayer->reloadList();
}
SearchFilterPopup* SearchFilterPopup::create(ModListLayer* layer, ModListType type) {
auto ret = new SearchFilterPopup();
if (ret && ret->init(350.f, 220.f, layer, type)) {

View file

@ -13,8 +13,12 @@ protected:
bool setup(ModListLayer* layer, ModListType type) override;
void addSearchMatch(const char* title, int flag, CCPoint& pos);
void addPlatformToggle(const char* title, PlatformID id, CCPoint& pos);
void onToggle(cocos2d::CCObject*);
void onSearchToggle(CCObject*);
void onPlatformToggle(CCObject*);
void onClose(CCObject*) override;
public:
static SearchFilterPopup* create(ModListLayer* layer, ModListType type);