mirror of
https://github.com/geode-sdk/geode.git
synced 2025-03-25 04:11:42 -04:00
filter mods to download by platform
This commit is contained in:
parent
9eee266e86
commit
59ada9e7f0
9 changed files with 158 additions and 62 deletions
loader
include/Geode/platform
src
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue