search filters are now good

This commit is contained in:
HJfod 2024-05-06 13:33:34 +03:00
parent c7c2cc7afb
commit 6215787ff6
13 changed files with 158 additions and 80 deletions

View file

@ -69,14 +69,17 @@ bool GeodeSquareSprite::init(CCSprite* top, bool* state) {
return true;
}
void GeodeSquareSprite::updateImage() {
this->setTexture(CCTextureCache::get()->addImage(
(m_state ? "GJ_button_02.png" : (isGeodeTheme() ? "GE_button_05.png"_spr : "GJ_button_01.png")),
true
));
}
void GeodeSquareSprite::update(float dt) {
CCSprite::update(dt);
if (m_stateSrc && m_state != *m_stateSrc) {
m_state = *m_stateSrc;
this->setTexture(CCTextureCache::get()->addImage(
(m_state ? "GJ_button_02.png" : (isGeodeTheme() ? "GE_button_05.png"_spr : "GJ_button_01.png")),
true
));
this->updateImage();
}
}
@ -89,7 +92,6 @@ GeodeSquareSprite* GeodeSquareSprite::create(const char* top, bool* state) {
CC_SAFE_DELETE(ret);
return nullptr;
}
GeodeSquareSprite* GeodeSquareSprite::createWithSpriteFrameName(const char* top, bool* state) {
auto ret = new GeodeSquareSprite();
if (ret && ret->init(CCSprite::createWithSpriteFrameName(top), state)) {
@ -104,6 +106,13 @@ CCSprite* GeodeSquareSprite::getTopSprite() const {
return m_topSprite;
}
void GeodeSquareSprite::setState(bool state) {
if (!m_stateSrc) {
m_state = state;
this->updateImage();
}
}
class LoadingSpinner : public CCNode {
protected:
CCSprite* m_spinner;

View file

@ -50,12 +50,14 @@ protected:
bool init(CCSprite* top, bool* state);
void update(float dt) override;
void updateImage();
public:
static GeodeSquareSprite* create(const char* top, bool* state = nullptr);
static GeodeSquareSprite* createWithSpriteFrameName(const char* top, bool* state = nullptr);
CCSprite* getTopSprite() const;
void setState(bool state);
};
CCNode* createLoadingCircle(float sideLength, const char* id = "loading-spinner");

View file

@ -200,24 +200,24 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
);
if (!typeinfo_cast<ServerModListSource*>(m_source)) {
sortBtn->setEnabled(false);
sortSpr->setColor({ 150, 150, 150 });
sortSpr->setColor(ccGRAY);
sortSpr->setOpacity(105);
sortSpr->getTopSprite()->setColor({ 150, 150, 150 });
sortSpr->getTopSprite()->setColor(ccGRAY);
sortSpr->getTopSprite()->setOpacity(105);
}
searchFiltersMenu->addChild(sortBtn);
auto filterBtn = CCMenuItemSpriteExtra::create(
m_filtersBtn = CCMenuItemSpriteExtra::create(
GeodeSquareSprite::createWithSpriteFrameName("GJ_filterIcon_001.png"),
this, menu_selector(ModList::onFilters)
);
searchFiltersMenu->addChild(filterBtn);
searchFiltersMenu->addChild(m_filtersBtn);
auto clearFiltersBtn = CCMenuItemSpriteExtra::create(
m_clearFiltersBtn = CCMenuItemSpriteExtra::create(
GeodeSquareSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png"),
this, menu_selector(ModList::onClearFilters)
);
searchFiltersMenu->addChild(clearFiltersBtn);
searchFiltersMenu->addChild(m_clearFiltersBtn);
searchFiltersMenu->setLayout(
RowLayout::create()
@ -522,6 +522,18 @@ void ModList::updateState() {
m_pagePrevBtn->setVisible(pageCount && m_page > 0);
m_pageNextBtn->setVisible(pageCount && m_page < pageCount.value() - 1);
// Update filter button states
auto isDefaultQuery = m_source->isDefaultQuery();
auto filterSpr = static_cast<GeodeSquareSprite*>(m_filtersBtn->getNormalImage());
filterSpr->setState(!isDefaultQuery);
auto clearSpr = static_cast<GeodeSquareSprite*>(m_clearFiltersBtn->getNormalImage());
clearSpr->setColor(isDefaultQuery ? ccGRAY : ccWHITE);
clearSpr->setOpacity(isDefaultQuery ? 90 : 255);
clearSpr->getTopSprite()->setColor(isDefaultQuery ? ccGRAY : ccWHITE);
clearSpr->getTopSprite()->setOpacity(isDefaultQuery ? 90 : 255);
// Post the update page number event
UpdateModListStateEvent(UpdatePageNumberState()).post();
}
@ -573,17 +585,13 @@ void ModList::showStatus(ModListStatus status, std::string const& message, std::
void ModList::onFilters(CCObject*) {
FiltersPopup::create(m_source)->show();
}
void ModList::onSort(CCObject*) {
SortPopup::create(m_source)->show();
}
void ModList::onClearFilters(CCObject*) {
// FIXME: reloads twice
m_source->setModTags({});
m_searchInput->setString("", true);
m_searchInput->setString("", false);
m_source->reset();
}
void ModList::onToggleUpdates(CCObject*) {
if (auto src = typeinfo_cast<InstalledModListSource*>(m_source)) {
auto mut = src->getQueryMut();
@ -592,7 +600,6 @@ void ModList::onToggleUpdates(CCObject*) {
InstalledModListType::OnlyUpdates;
}
}
void ModList::onToggleErrors(CCObject*) {
if (auto src = typeinfo_cast<InstalledModListSource*>(m_source)) {
auto mut = src->getQueryMut();
@ -601,7 +608,6 @@ void ModList::onToggleErrors(CCObject*) {
InstalledModListType::OnlyErrors;
}
}
void ModList::onUpdateAll(CCObject*) {
server::ModDownloadManager::get()->startUpdateAll();
}

View file

@ -42,6 +42,8 @@ protected:
CCMenuItemToggler* m_toggleErrorsOnlyBtn = nullptr;
TextArea* m_updateCountLabel = nullptr;
TextInput* m_searchInput;
CCMenuItemSpriteExtra* m_filtersBtn;
CCMenuItemSpriteExtra* m_clearFiltersBtn;
EventListener<InvalidateCacheFilter> m_invalidateCacheListener;
EventListener<server::ServerRequest<std::vector<std::string>>> m_checkUpdatesListener;
EventListener<server::ModDownloadFilter> m_downloadListener;

View file

@ -10,7 +10,7 @@ bool DevListPopup::setup(ModMetadata const& meta) {
for (auto dev : meta.getDevelopers()) {
auto menu = CCMenu::create();
menu->setContentWidth(90);
menu->setContentWidth(m_size.width - 30);
auto label = CCLabelBMFont::create(dev.c_str(), "bigFont.fnt");
label->setLayoutOptions(AxisLayoutOptions::create()->setScalePriority(1));
@ -55,7 +55,7 @@ void DevListPopup::onMoreByThisDev(CCObject* sender) {
DevListPopup* DevListPopup::create(ModMetadata const& meta) {
auto ret = new DevListPopup();
if (ret && ret->init(200, 180, meta)) {
if (ret && ret->init(220, 220, meta)) {
ret->autorelease();
return ret;
}

View file

@ -1,5 +1,4 @@
#include "FiltersPopup.hpp"
#include <Geode/ui/TextInput.hpp>
bool FiltersPopup::setup(ModListSource* src) {
m_noElasticity = true;
@ -55,68 +54,82 @@ bool FiltersPopup::setup(ModListSource* src) {
m_mainLayer->addChildAtPosition(tagsContainer, Anchor::Top, ccp(0, -85));
auto optionsContainer = CCNode::create();
optionsContainer->setContentSize(ccp(160, 35));
optionsContainer->setAnchorPoint({ .5f, .5f });
optionsContainer->setVisible(false);
if (auto src = typeinfo_cast<InstalledModListSource*>(m_source)) {
auto optionsContainer = CCNode::create();
optionsContainer->setContentSize(ccp(160, 35));
optionsContainer->setAnchorPoint({ .5f, .5f });
auto optionsBG = CCScale9Sprite::create("square02b_001.png");
optionsBG->setColor({ 0, 0, 0 });
optionsBG->setOpacity(75);
optionsBG->setScale(.3f);
optionsBG->setContentSize(optionsContainer->getContentSize() / optionsBG->getScale());
optionsContainer->addChildAtPosition(optionsBG, Anchor::Center);
auto optionsBG = CCScale9Sprite::create("square02b_001.png");
optionsBG->setColor({ 0, 0, 0 });
optionsBG->setOpacity(75);
optionsBG->setScale(.3f);
optionsBG->setContentSize(optionsContainer->getContentSize() / optionsBG->getScale());
optionsContainer->addChildAtPosition(optionsBG, Anchor::Center);
auto optionsMenu = CCMenu::create();
optionsMenu->setContentSize(optionsContainer->getContentSize() - ccp(10, 10));
auto optionsMenu = CCMenu::create();
optionsMenu->setContentSize(optionsContainer->getContentSize() - ccp(10, 10));
if (typeinfo_cast<InstalledModListSource*>(m_source)) {
auto enabledOnlyToggle = CCMenuItemToggler::createWithStandardSprites(
this, menu_selector(FiltersPopup::onToggle), .6f
);
enabledOnlyToggle->m_notClickable = true;
enabledOnlyToggle->setUserData(reinterpret_cast<void*>(+[](ModListSource* src) -> bool {
auto mut = static_cast<InstalledModListSource*>(src)->getQueryMut();
mut->enabledOnly = mut->enabledOnly.has_value() ? std::nullopt : std::optional(true);
return mut->enabledOnly.value_or(false);
}));
enabledOnlyToggle->toggle(static_cast<InstalledModListSource*>(src)->getQuery().enabledOnly.has_value());
optionsMenu->addChildAtPosition(enabledOnlyToggle, Anchor::Left, ccp(15, 0));
m_enabledModsOnly = CCMenuItemToggler::createWithStandardSprites(this, nullptr, .6f);
m_enabledModsOnly->toggle(src->getQuery().enabledOnly.has_value());
optionsMenu->addChildAtPosition(m_enabledModsOnly, Anchor::Left, ccp(15, 0));
auto enabledOnlyLabel = CCLabelBMFont::create("Enabled Mods Only", "bigFont.fnt");
enabledOnlyLabel->setScale(.35f);
optionsMenu->addChildAtPosition(enabledOnlyLabel, Anchor::Left, ccp(30, 0), ccp(0, .5f));
optionsContainer->setVisible(true);
optionsContainer->addChildAtPosition(optionsMenu, Anchor::Center);
auto optionsTitleMenu = CCMenu::create();
optionsTitleMenu->setAnchorPoint({ .5f, 0 });
optionsTitleMenu->setContentWidth(optionsContainer->getContentWidth());
auto optionsTitle = CCLabelBMFont::create("Options", "bigFont.fnt");
optionsTitleMenu->addChild(optionsTitle);
optionsTitleMenu->addChild(SpacerNode::create());
optionsTitleMenu->setLayout(
RowLayout::create()
->setDefaultScaleLimits(.1f, .4f)
);
optionsContainer->addChildAtPosition(optionsTitleMenu, Anchor::Top, ccp(0, 4));
m_mainLayer->addChildAtPosition(optionsContainer, Anchor::Bottom, ccp(0, 60), ccp(.5f, .5f));
}
else if (typeinfo_cast<ServerModListSource*>(m_source)) {
auto input = TextInput::create(100, "Developer");
input->setTextAlign(TextInputAlign::Left);
input->setLabel("Developer Name");
optionsMenu->addChildAtPosition(input, Anchor::Left, ccp(15, 0));
else if (auto src = typeinfo_cast<ServerModListSource*>(m_source)) {
auto inputContainer = CCNode::create();
inputContainer->setContentSize(ccp(160, 35));
inputContainer->setAnchorPoint({ .5f, .5f });
optionsContainer->setVisible(true);
m_developerNameInput = TextInput::create(inputContainer->getContentWidth(), "Developer Name");
m_developerNameInput->setTextAlign(TextInputAlign::Left);
m_developerNameInput->setString(src->getQuery().developer.value_or(""));
inputContainer->addChildAtPosition(m_developerNameInput, Anchor::Center);
auto inputTitleMenu = CCMenu::create();
inputTitleMenu->setAnchorPoint({ .5f, 0 });
inputTitleMenu->setContentWidth(inputContainer->getContentWidth());
auto inputTitle = CCLabelBMFont::create("From Developer", "bigFont.fnt");
inputTitleMenu->addChild(inputTitle);
inputTitleMenu->addChild(SpacerNode::create());
auto resetSpr = CCSprite::createWithSpriteFrameName("GJ_trashBtn_001.png");
auto resetBtn = CCMenuItemSpriteExtra::create(
resetSpr, this, menu_selector(FiltersPopup::onResetDevName)
);
inputTitleMenu->addChild(resetBtn);
inputTitleMenu->setLayout(
RowLayout::create()
->setDefaultScaleLimits(.1f, .4f)
);
inputContainer->addChildAtPosition(inputTitleMenu, Anchor::Top, ccp(0, 4));
m_mainLayer->addChildAtPosition(inputContainer, Anchor::Bottom, ccp(0, 60), ccp(.5f, .5f));
}
optionsContainer->addChildAtPosition(optionsMenu, Anchor::Center);
auto optionsTitleMenu = CCMenu::create();
optionsTitleMenu->setAnchorPoint({ .5f, 0 });
optionsTitleMenu->setContentWidth(optionsContainer->getContentWidth());
auto optionsTitle = CCLabelBMFont::create("Options", "bigFont.fnt");
optionsTitleMenu->addChild(optionsTitle);
optionsTitleMenu->addChild(SpacerNode::create());
optionsTitleMenu->setLayout(
RowLayout::create()
->setDefaultScaleLimits(.1f, .4f)
);
optionsContainer->addChildAtPosition(optionsTitleMenu, Anchor::Top, ccp(0, 4));
m_mainLayer->addChildAtPosition(optionsContainer, Anchor::Bottom, ccp(0, 60), ccp(.5f, .5f));
auto okSpr = createGeodeButton("OK");
okSpr->setScale(.7f);
auto okBtn = CCMenuItemSpriteExtra::create(
@ -158,11 +171,6 @@ void FiltersPopup::onLoadTags(typename server::ServerRequest<std::unordered_set<
}
}
void FiltersPopup::onToggle(CCObject* sender) {
auto toggle = static_cast<CCMenuItemToggler*>(sender);
auto enabled = reinterpret_cast<bool(*)(ModListSource*)>(toggle->getUserData())(m_source);
toggle->toggle(enabled);
}
void FiltersPopup::updateTags() {
for (auto node : CCArrayExt<CCNode*>(m_tagsMenu->getChildren())) {
if (auto toggle = typeinfo_cast<CCMenuItemToggler*>(node)) {
@ -186,8 +194,22 @@ void FiltersPopup::onResetTags(CCObject*) {
m_selectedTags.clear();
this->updateTags();
}
void FiltersPopup::onResetDevName(CCObject*) {
if (m_developerNameInput) {
m_developerNameInput->setString("");
}
}
void FiltersPopup::onClose(CCObject* sender) {
m_source->setModTags(m_selectedTags);
if (auto src = typeinfo_cast<InstalledModListSource*>(m_source)) {
src->getQueryMut()->enabledOnly = m_enabledModsOnly->isToggled() ? std::optional(true) : std::nullopt;
}
else if (auto src = typeinfo_cast<ServerModListSource*>(m_source)) {
src->getQueryMut()->developer = m_developerNameInput->getString();
if (src->getQueryMut()->developer->empty()) {
src->getQueryMut()->developer.reset();
}
}
Popup::onClose(sender);
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <Geode/ui/Popup.hpp>
#include <Geode/ui/TextInput.hpp>
#include "../sources/ModListSource.hpp"
#include "../GeodeStyle.hpp"
#include <server/Server.hpp>
@ -13,13 +14,15 @@ protected:
CCMenu* m_tagsMenu;
std::unordered_set<std::string> m_selectedTags;
EventListener<server::ServerRequest<std::unordered_set<std::string>>> m_tagsListener;
CCMenuItemToggler* m_enabledModsOnly = nullptr;
TextInput* m_developerNameInput = nullptr;
bool setup(ModListSource* src) override;
void updateTags();
void onToggle(CCObject*);
void onLoadTags(typename server::ServerRequest<std::unordered_set<std::string>>::Event* event);
void onResetTags(CCObject*);
void onResetDevName(CCObject*);
void onSelectTag(CCObject* sender);
void onClose(CCObject* sender) override;

View file

@ -32,6 +32,9 @@ bool InstalledModsQuery::queryCheck(ModSource const& src, double& weighted) cons
// todo: favorites
return addToList;
}
bool InstalledModsQuery::isDefault() const {
return LocalModsQueryBase::isDefault() && !enabledOnly.has_value();
}
InstalledModListSource::InstalledModListSource(InstalledModListType type)
: m_type(type)
@ -116,3 +119,6 @@ InstalledModsQuery const& InstalledModListSource::getQuery() const {
InvalidateQueryAfter<InstalledModsQuery> InstalledModListSource::getQueryMut() {
return InvalidateQueryAfter(m_query, this);
}
bool InstalledModListSource::isDefaultQuery() const {
return m_query.isDefault();
}

View file

@ -23,6 +23,10 @@ ListenerResult InvalidateCacheFilter::handle(MiniFunction<Callback> fn, Invalida
InvalidateCacheFilter::InvalidateCacheFilter(ModListSource* src) : m_source(src) {}
bool LocalModsQueryBase::isDefault() const {
return !query.has_value() && tags.empty();
}
typename ModListSource::PageLoadTask ModListSource::loadPage(size_t page, bool forceUpdate) {
if (!forceUpdate && m_cachedPages.contains(page)) {
return PageLoadTask::immediate(Ok(m_cachedPages.at(page)));
@ -59,8 +63,8 @@ std::optional<size_t> ModListSource::getItemCount() const {
}
void ModListSource::reset() {
this->clearCache();
this->resetQuery();
this->clearCache();
}
void ModListSource::clearCache() {
m_cachedPages.clear();

View file

@ -65,6 +65,7 @@ public:
void reset();
void clearCache();
void search(std::string const& query);
virtual bool isDefaultQuery() const = 0;
virtual std::unordered_set<std::string> getModTags() const = 0;
virtual void setModTags(std::unordered_set<std::string> const& tags) = 0;
@ -97,9 +98,9 @@ public:
struct LocalModsQueryBase {
std::optional<std::string> query;
std::unordered_set<std::string> tags = {};
std::optional<bool> enabledOnly;
size_t page = 0;
size_t pageSize = 10;
bool isDefault() const;
};
enum class InstalledModListType {
@ -109,8 +110,10 @@ enum class InstalledModListType {
};
struct InstalledModsQuery final : public LocalModsQueryBase {
InstalledModListType type = InstalledModListType::All;
std::optional<bool> enabledOnly;
bool preCheck(ModSource const& src) const;
bool queryCheck(ModSource const& src, double& weighted) const;
bool isDefault() const;
};
class InstalledModListSource : public ModListSource {
@ -132,11 +135,13 @@ public:
InstalledModsQuery const& getQuery() const;
InvalidateQueryAfter<InstalledModsQuery> getQueryMut();
bool isDefaultQuery() const override;
};
struct SuggestedModsQuery final : public LocalModsQueryBase {
bool preCheck(ModSource const& src) const;
bool queryCheck(ModSource const& src, double& weighted) const;
bool isDefault() const;
};
class SuggestedModListSource : public ModListSource {
@ -154,6 +159,7 @@ public:
std::unordered_set<std::string> getModTags() const override;
void setModTags(std::unordered_set<std::string> const& tags) override;
bool isDefaultQuery() const override;
};
enum class ServerModListType {
@ -182,6 +188,7 @@ public:
server::ModsQuery const& getQuery() const;
InvalidateQueryAfter<server::ModsQuery> getQueryMut();
bool isDefaultQuery() const override;
};
class ModPackListSource : public ModListSource {
@ -197,6 +204,7 @@ public:
std::unordered_set<std::string> getModTags() const override;
void setModTags(std::unordered_set<std::string> const& tags) override;
bool isDefaultQuery() const override;
};
bool weightedFuzzyMatch(std::string const& str, std::string const& kw, double weight, double& out);

View file

@ -18,3 +18,6 @@ std::unordered_set<std::string> ModPackListSource::getModTags() const {
return {};
}
void ModPackListSource::setModTags(std::unordered_set<std::string> const& set) {}
bool ModPackListSource::isDefaultQuery() const {
return true;
}

View file

@ -97,3 +97,9 @@ server::ModsQuery const& ServerModListSource::getQuery() const {
InvalidateQueryAfter<server::ModsQuery> ServerModListSource::getQueryMut() {
return InvalidateQueryAfter(m_query, this);
}
bool ServerModListSource::isDefaultQuery() const {
return !m_query.query.has_value() &&
!m_query.featured.has_value() &&
m_query.tags.empty() &&
!m_query.developer.has_value();
}

View file

@ -13,6 +13,9 @@ bool SuggestedModsQuery::queryCheck(ModSource const& src, double& weighted) cons
}
return true;
}
bool SuggestedModsQuery::isDefault() const {
return LocalModsQueryBase::isDefault();
}
void SuggestedModListSource::resetQuery() {
m_query = SuggestedModsQuery();
@ -58,3 +61,7 @@ void SuggestedModListSource::setModTags(std::unordered_set<std::string> const& t
m_query.tags = tags;
this->clearCache();
}
bool SuggestedModListSource::isDefaultQuery() const {
return m_query.isDefault();
}