index work!!

- add tag support for index
 - rename CategoryNode to TagNode
 - add them back to the UI + fix spacing issues related to them
 - deleted ModListView, now using a generic ListView with the mod cells added as children to it
This commit is contained in:
HJfod 2022-12-08 21:15:06 +02:00
parent e763e271bf
commit 84bdeb7beb
14 changed files with 339 additions and 408 deletions

View file

@ -58,6 +58,7 @@ namespace geode {
std::unordered_set<PlatformID> platforms; std::unordered_set<PlatformID> platforms;
} download; } download;
bool isFeatured; bool isFeatured;
std::unordered_set<std::string> tags;
/** /**
* Create IndexItem from a directory * Create IndexItem from a directory

View file

@ -122,6 +122,7 @@ Result<IndexItemHandle> IndexItem::createFromDir(
.platforms = platforms, .platforms = platforms,
}, },
.isFeatured = root.has("is-featured").template get<bool>(), .isFeatured = root.has("is-featured").template get<bool>(),
.tags = root.has("tags").template get<std::unordered_set<std::string>>()
}); });
if (checker.isError()) { if (checker.isError()) {
return Err(checker.getError()); return Err(checker.getError());

View file

@ -1,22 +0,0 @@
#pragma once
#include <cocos2d.h>
USE_GEODE_NAMESPACE();
enum class CategoryNodeStyle {
Tag,
Dot,
};
class CategoryNode : public CCNode {
protected:
bool init(std::string const& category, CategoryNodeStyle style);
public:
static CategoryNode* create(
std::string const& category, CategoryNodeStyle style = CategoryNodeStyle::Tag
);
static ccColor3B categoryToColor(std::string const& category);
};

View file

@ -1,7 +1,7 @@
#include "ModInfoPopup.hpp" #include "ModInfoPopup.hpp"
#include "../dev/HookListLayer.hpp" #include "../dev/HookListLayer.hpp"
#include "../list/ModListView.hpp" #include "../list/ModListLayer.hpp"
#include "../settings/ModSettingsPopup.hpp" #include "../settings/ModSettingsPopup.hpp"
#include "../settings/AdvancedSettingsPopup.hpp" #include "../settings/AdvancedSettingsPopup.hpp"
#include <InternalLoader.hpp> #include <InternalLoader.hpp>
@ -17,6 +17,7 @@
#include <Geode/ui/BasedButton.hpp> #include <Geode/ui/BasedButton.hpp>
#include <Geode/ui/IconButtonSprite.hpp> #include <Geode/ui/IconButtonSprite.hpp>
#include <Geode/ui/GeodeUI.hpp> #include <Geode/ui/GeodeUI.hpp>
#include <Geode/ui/MDPopup.hpp>
#include <Geode/utils/casts.hpp> #include <Geode/utils/casts.hpp>
#include <Geode/utils/ranges.hpp> #include <Geode/utils/ranges.hpp>
#include <Geode/utils/web.hpp> #include <Geode/utils/web.hpp>
@ -30,9 +31,9 @@ static constexpr int const TAG_CONFIRM_UNINSTALL = 5;
static constexpr int const TAG_DELETE_SAVEDATA = 6; static constexpr int const TAG_DELETE_SAVEDATA = 6;
static const CCSize LAYER_SIZE = { 440.f, 290.f }; static const CCSize LAYER_SIZE = { 440.f, 290.f };
bool ModInfoPopup::init(ModInfo const& info, ModListView* list) { bool ModInfoPopup::init(ModInfo const& info, ModListLayer* list) {
m_noElasticity = true; m_noElasticity = true;
m_list = list; m_layer = list;
auto winSize = CCDirector::sharedDirector()->getWinSize(); auto winSize = CCDirector::sharedDirector()->getWinSize();
@ -274,7 +275,7 @@ void ModInfoPopup::onClose(CCObject* pSender) {
// LocalModInfoPopup // LocalModInfoPopup
bool LocalModInfoPopup::init(Mod* mod, ModListView* list) { bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
m_mod = mod; m_mod = mod;
if (!ModInfoPopup::init(mod->getModInfo(), list)) if (!ModInfoPopup::init(mod->getModInfo(), list))
@ -458,7 +459,7 @@ void LocalModInfoPopup::onEnableMod(CCObject* sender) {
"OK" "OK"
) )
->show(); ->show();
if (m_list) m_list->updateAllStates(nullptr); if (m_layer) m_layer->updateAllStates(nullptr);
return; return;
} }
if (as<CCMenuItemToggler*>(sender)->isToggled()) { if (as<CCMenuItemToggler*>(sender)->isToggled()) {
@ -479,7 +480,7 @@ void LocalModInfoPopup::onEnableMod(CCObject* sender) {
)->show(); )->show();
} }
} }
if (m_list) m_list->updateAllStates(nullptr); if (m_layer) m_layer->updateAllStates(nullptr);
as<CCMenuItemToggler*>(sender)->toggle(m_mod->isEnabled()); as<CCMenuItemToggler*>(sender)->toggle(m_mod->isEnabled());
} }
@ -530,7 +531,7 @@ void LocalModInfoPopup::FLAlert_Clicked(FLAlertLayer* layer, bool btn2) {
)->show(); )->show();
} }
} }
if (m_list) m_list->refreshList(); if (m_layer) m_layer->reloadList();
this->onClose(nullptr); this->onClose(nullptr);
} break; } break;
} }
@ -556,7 +557,7 @@ void LocalModInfoPopup::uninstall() {
layer->show(); layer->show();
} }
LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListView* list) { LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListLayer* list) {
auto ret = new LocalModInfoPopup; auto ret = new LocalModInfoPopup;
if (ret && ret->init(mod, list)) { if (ret && ret->init(mod, list)) {
ret->autorelease(); ret->autorelease();
@ -568,7 +569,7 @@ LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListView* list) {
// IndexItemInfoPopup // IndexItemInfoPopup
bool IndexItemInfoPopup::init(IndexItemHandle item, ModListView* list) { bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
m_item = item; m_item = item;
auto winSize = CCDirector::sharedDirector()->getWinSize(); auto winSize = CCDirector::sharedDirector()->getWinSize();
@ -608,7 +609,7 @@ ModInfo IndexItemInfoPopup::getModInfo() const {
} }
IndexItemInfoPopup* IndexItemInfoPopup::create( IndexItemInfoPopup* IndexItemInfoPopup::create(
IndexItemHandle item, ModListView* list IndexItemHandle item, ModListLayer* list
) { ) {
auto ret = new IndexItemInfoPopup; auto ret = new IndexItemInfoPopup;
if (ret && ret->init(item, list)) { if (ret && ret->init(item, list)) {

View file

@ -9,7 +9,7 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
class ModListView; class ModListLayer;
class ModObject; class ModObject;
class DownloadStatusNode : public CCNode { class DownloadStatusNode : public CCNode {
@ -28,7 +28,7 @@ public:
class ModInfoPopup : public FLAlertLayer { class ModInfoPopup : public FLAlertLayer {
protected: protected:
ModListView* m_list = nullptr; ModListLayer* m_layer = nullptr;
DownloadStatusNode* m_installStatus = nullptr; DownloadStatusNode* m_installStatus = nullptr;
IconButtonSprite* m_installBtnSpr; IconButtonSprite* m_installBtnSpr;
CCMenuItemSpriteExtra* m_installBtn; CCMenuItemSpriteExtra* m_installBtn;
@ -43,7 +43,7 @@ protected:
void onSupport(CCObject*); void onSupport(CCObject*);
void onInfo(CCObject*); void onInfo(CCObject*);
bool init(ModInfo const& info, ModListView* list); bool init(ModInfo const& info, ModListLayer* list);
void keyDown(cocos2d::enumKeyCodes) override; void keyDown(cocos2d::enumKeyCodes) override;
void onClose(cocos2d::CCObject*); void onClose(cocos2d::CCObject*);
@ -56,7 +56,7 @@ class LocalModInfoPopup : public ModInfoPopup, public FLAlertLayerProtocol {
protected: protected:
Mod* m_mod; Mod* m_mod;
bool init(Mod* mod, ModListView* list); bool init(Mod* mod, ModListLayer* list);
void onIssues(CCObject*); void onIssues(CCObject*);
void onSettings(CCObject*); void onSettings(CCObject*);
@ -74,18 +74,18 @@ protected:
ModInfo getModInfo() const override; ModInfo getModInfo() const override;
public: public:
static LocalModInfoPopup* create(Mod* mod, ModListView* list); static LocalModInfoPopup* create(Mod* mod, ModListLayer* list);
}; };
class IndexItemInfoPopup : public ModInfoPopup { class IndexItemInfoPopup : public ModInfoPopup {
protected: protected:
IndexItemHandle m_item; IndexItemHandle m_item;
bool init(IndexItemHandle item, ModListView* list); bool init(IndexItemHandle item, ModListLayer* list);
CCNode* createLogo(CCSize const& size) override; CCNode* createLogo(CCSize const& size) override;
ModInfo getModInfo() const override; ModInfo getModInfo() const override;
public: public:
static IndexItemInfoPopup* create(IndexItemHandle item, ModListView* list); static IndexItemInfoPopup* create(IndexItemHandle item, ModListLayer* list);
}; };

View file

@ -1,6 +1,9 @@
#include "CategoryNode.hpp" #include "TagNode.hpp"
#include <Geode/utils/general.hpp>
#include <cocos-ext.h>
#include <Geode/loader/Mod.hpp>
ccColor3B CategoryNode::categoryToColor(std::string const& category) { ccColor3B TagNode::categoryToColor(std::string const& category) {
// all we need is to convert a string into some number // all we need is to convert a string into some number
// between 0 and 360 and for that number to always be the // between 0 and 360 and for that number to always be the
// same for the same string // same for the same string
@ -17,8 +20,8 @@ ccColor3B CategoryNode::categoryToColor(std::string const& category) {
static_cast<GLubyte>(rgb.b * 255) }; static_cast<GLubyte>(rgb.b * 255) };
} }
bool CategoryNode::init(std::string const& category, CategoryNodeStyle style) { bool TagNode::init(std::string const& category, TagNodeStyle style) {
if (style == CategoryNodeStyle::Dot) { if (style == TagNodeStyle::Dot) {
auto dot = CCSprite::createWithSpriteFrameName("category-dot.png"_spr); auto dot = CCSprite::createWithSpriteFrameName("category-dot.png"_spr);
dot->setColor(categoryToColor(category)); dot->setColor(categoryToColor(category));
dot->setPosition({ 20.f, 20.f }); dot->setPosition({ 20.f, 20.f });
@ -52,8 +55,8 @@ bool CategoryNode::init(std::string const& category, CategoryNodeStyle style) {
return true; return true;
} }
CategoryNode* CategoryNode::create(std::string const& category, CategoryNodeStyle style) { TagNode* TagNode::create(std::string const& category, TagNodeStyle style) {
auto ret = new CategoryNode(); auto ret = new TagNode();
if (ret && ret->init(category, style)) { if (ret && ret->init(category, style)) {
ret->autorelease(); ret->autorelease();
return ret; return ret;

View file

@ -0,0 +1,22 @@
#pragma once
#include <cocos2d.h>
USE_GEODE_NAMESPACE();
enum class TagNodeStyle {
Tag,
Dot,
};
class TagNode : public CCNode {
protected:
bool init(std::string const& category, TagNodeStyle style);
public:
static TagNode* create(
std::string const& category, TagNodeStyle style = TagNodeStyle::Tag
);
static ccColor3B categoryToColor(std::string const& category);
};

View file

@ -1,5 +1,5 @@
#include "ModListCell.hpp" #include "ModListCell.hpp"
#include "ModListView.hpp" #include "ModListLayer.hpp"
#include "../info/ModInfoPopup.hpp" #include "../info/ModInfoPopup.hpp"
#include <Geode/binding/StatsCell.hpp> #include <Geode/binding/StatsCell.hpp>
#include <Geode/binding/FLAlertLayer.hpp> #include <Geode/binding/FLAlertLayer.hpp>
@ -8,6 +8,7 @@
#include <Geode/binding/CCMenuItemToggler.hpp> #include <Geode/binding/CCMenuItemToggler.hpp>
#include <Geode/ui/GeodeUI.hpp> #include <Geode/ui/GeodeUI.hpp>
#include <InternalLoader.hpp> #include <InternalLoader.hpp>
#include "../info/TagNode.hpp"
template <class T> template <class T>
static bool tryOrAlert(Result<T> const& res, char const* title) { static bool tryOrAlert(Result<T> const& res, char const* title) {
@ -17,45 +18,46 @@ static bool tryOrAlert(Result<T> const& res, char const* title) {
return res.isOk(); return res.isOk();
} }
ModListCell::ModListCell(char const* name, CCSize const& size)
: TableViewCell(name, size.width, size.height) {}
void ModListCell::draw() { void ModListCell::draw() {
reinterpret_cast<StatsCell*>(this)->StatsCell::draw(); reinterpret_cast<StatsCell*>(this)->StatsCell::draw();
} }
void ModListCell::setupInfo(ModInfo const& info, bool spaceForCategories) { float ModListCell::getLogoSize() const {
m_mainLayer->setVisible(true); return m_height / 1.5f;
m_backgroundLayer->setOpacity(255); }
void ModListCell::setupInfo(ModInfo const& info, bool spaceForTags) {
m_menu = CCMenu::create(); m_menu = CCMenu::create();
m_menu->setPosition(m_width - 40.f, m_height / 2); m_menu->setPosition(m_width - 40.f, m_height / 2);
m_mainLayer->addChild(m_menu); this->addChild(m_menu);
auto logoSize = m_height / 1.5f; auto logoSize = this->getLogoSize();
auto logoSpr = this->createLogo({ logoSize, logoSize }); auto logoSpr = this->createLogo({ logoSize, logoSize });
logoSpr->setPosition({ logoSize / 2 + 12.f, m_height / 2 }); logoSpr->setPosition({ logoSize / 2 + 12.f, m_height / 2 });
m_mainLayer->addChild(logoSpr); this->addChild(logoSpr);
bool hasDesc = bool hasDesc =
m_display == ModListDisplay::Expanded && m_layer->getDisplay() == ModListDisplay::Expanded &&
info.m_description.has_value(); info.m_description.has_value();
auto titleLabel = CCLabelBMFont::create(info.m_name.c_str(), "bigFont.fnt"); auto titleLabel = CCLabelBMFont::create(info.m_name.c_str(), "bigFont.fnt");
titleLabel->setAnchorPoint({ .0f, .5f }); titleLabel->setAnchorPoint({ .0f, .5f });
titleLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f); titleLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f);
if (hasDesc && spaceForCategories) { if (hasDesc && spaceForTags) {
titleLabel->setPositionY(m_height / 2 + 20.f); titleLabel->setPositionY(m_height / 2 + 20.f);
} }
else if (hasDesc || spaceForCategories) { else if (spaceForTags) {
titleLabel->setPositionY(m_height / 2 + 12.f);
}
else if (hasDesc) {
titleLabel->setPositionY(m_height / 2 + 15.f); titleLabel->setPositionY(m_height / 2 + 15.f);
} }
else { else {
titleLabel->setPositionY(m_height / 2 + 7.f); titleLabel->setPositionY(m_height / 2 + 7.f);
} }
titleLabel->limitLabelWidth(m_width / 2 - 40.f, .5f, .1f); titleLabel->limitLabelWidth(m_width / 2 - 40.f, .5f, .1f);
m_mainLayer->addChild(titleLabel); this->addChild(titleLabel);
auto versionLabel = CCLabelBMFont::create(info.m_version.toString().c_str(), "bigFont.fnt"); auto versionLabel = CCLabelBMFont::create(info.m_version.toString().c_str(), "bigFont.fnt");
versionLabel->setAnchorPoint({ .0f, .5f }); versionLabel->setAnchorPoint({ .0f, .5f });
@ -65,23 +67,23 @@ void ModListCell::setupInfo(ModInfo const& info, bool spaceForCategories) {
titleLabel->getPositionY() - 1.f titleLabel->getPositionY() - 1.f
); );
versionLabel->setColor({ 0, 255, 0 }); versionLabel->setColor({ 0, 255, 0 });
m_mainLayer->addChild(versionLabel); this->addChild(versionLabel);
auto creatorStr = "by " + info.m_developer; auto creatorStr = "by " + info.m_developer;
auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt"); auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt");
creatorLabel->setAnchorPoint({ .0f, .5f }); creatorLabel->setAnchorPoint({ .0f, .5f });
creatorLabel->setScale(.43f); creatorLabel->setScale(.43f);
creatorLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f); creatorLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f);
if (hasDesc && spaceForCategories) { if (hasDesc && spaceForTags) {
creatorLabel->setPositionY(m_height / 2 + 7.5f); creatorLabel->setPositionY(m_height / 2 + 7.5f);
} }
else if (hasDesc || spaceForCategories) { else if (hasDesc || spaceForTags) {
creatorLabel->setPositionY(m_height / 2); creatorLabel->setPositionY(m_height / 2);
} }
else { else {
creatorLabel->setPositionY(m_height / 2 - 7.f); creatorLabel->setPositionY(m_height / 2 - 7.f);
} }
m_mainLayer->addChild(creatorLabel); this->addChild(creatorLabel);
if (hasDesc) { if (hasDesc) {
auto descBG = CCScale9Sprite::create("square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f }); auto descBG = CCScale9Sprite::create("square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f });
@ -90,48 +92,41 @@ void ModListCell::setupInfo(ModInfo const& info, bool spaceForCategories) {
descBG->setContentSize({ m_width * 2, 60.f }); descBG->setContentSize({ m_width * 2, 60.f });
descBG->setAnchorPoint({ .0f, .5f }); descBG->setAnchorPoint({ .0f, .5f });
descBG->setPositionX(m_height / 2 + logoSize / 2 + 13.f); descBG->setPositionX(m_height / 2 + logoSize / 2 + 13.f);
if (spaceForCategories) { if (spaceForTags) {
descBG->setPositionY(m_height / 2 - 7.5f); descBG->setPositionY(m_height / 2 - 7.5f);
} }
else { else {
descBG->setPositionY(m_height / 2 - 17.f); descBG->setPositionY(m_height / 2 - 17.f);
} }
descBG->setScale(.25f); descBG->setScale(.25f);
m_mainLayer->addChild(descBG); this->addChild(descBG);
auto descText = CCLabelBMFont::create(info.m_description.value().c_str(), "chatFont.fnt"); m_description = CCLabelBMFont::create(info.m_description.value().c_str(), "chatFont.fnt");
descText->setAnchorPoint({ .0f, .5f }); m_description->setAnchorPoint({ .0f, .5f });
descText->setPosition(m_height / 2 + logoSize / 2 + 18.f, descBG->getPositionY()); m_description->setPosition(m_height / 2 + logoSize / 2 + 18.f, descBG->getPositionY());
descText->limitLabelWidth(m_width / 2 - 10.f, .5f, .1f); m_description->limitLabelWidth(m_width / 2 - 10.f, .5f, .1f);
m_mainLayer->addChild(descText); this->addChild(m_description);
} }
} }
void ModListCell::updateBGColor(int index) { bool ModListCell::init(ModListLayer* list, CCSize const& size) {
if (index % 2) { m_width = size.width;
m_backgroundLayer->setColor(ccc3(0xc2, 0x72, 0x3e)); m_height = size.height;
} m_layer = list;
else m_backgroundLayer->setColor(ccc3(0xa1, 0x58, 0x2c)); this->setContentSize(size);
m_backgroundLayer->setOpacity(0xff); this->setID("mod-list-cell");
}
bool ModListCell::init(ModListView* list, ModListDisplay display) {
m_list = list;
m_display = display;
return true; return true;
} }
// ModCell // ModCell
ModCell::ModCell(const char* name, CCSize const& size)
: ModListCell(name, size) {}
ModCell* ModCell::create( ModCell* ModCell::create(
ModListView* list, ModListDisplay display, Mod* mod,
const char* key, CCSize const& size ModListLayer* list,
CCSize const& size
) { ) {
auto ret = new ModCell(key, size); auto ret = new ModCell();
if (ret && ret->init(list, display)) { if (ret && ret->init(mod, list, size)) {
return ret; return ret;
} }
CC_SAFE_DELETE(ret); CC_SAFE_DELETE(ret);
@ -148,7 +143,7 @@ void ModCell::onEnable(CCObject* sender) {
"need to <cg>restart</c> the game to have it fully unloaded.", "need to <cg>restart</c> the game to have it fully unloaded.",
"OK" "OK"
)->show(); )->show();
m_list->updateAllStates(this); m_layer->updateAllStates(this);
return; return;
} }
if (!as<CCMenuItemToggler*>(sender)->isToggled()) { if (!as<CCMenuItemToggler*>(sender)->isToggled()) {
@ -157,7 +152,7 @@ void ModCell::onEnable(CCObject* sender) {
else { else {
tryOrAlert(m_mod->disable(), "Error disabling mod"); tryOrAlert(m_mod->disable(), "Error disabling mod");
} }
m_list->updateAllStates(this); m_layer->updateAllStates(this);
} }
void ModCell::onUnresolvedInfo(CCObject*) { void ModCell::onUnresolvedInfo(CCObject*) {
@ -176,7 +171,7 @@ void ModCell::onUnresolvedInfo(CCObject*) {
} }
void ModCell::onInfo(CCObject*) { void ModCell::onInfo(CCObject*) {
LocalModInfoPopup::create(m_mod, m_list)->show(); LocalModInfoPopup::create(m_mod, m_layer)->show();
} }
void ModCell::updateState() { void ModCell::updateState() {
@ -192,7 +187,14 @@ void ModCell::updateState() {
m_unresolvedExMark->setVisible(unresolved); m_unresolvedExMark->setVisible(unresolved);
} }
void ModCell::loadFromMod(Mod* mod) { bool ModCell::init(
Mod* mod,
ModListLayer* list,
CCSize const& size
) {
if (!ModListCell::init(list, size))
return false;
m_mod = mod; m_mod = mod;
this->setupInfo(mod->getModInfo(), false); this->setupInfo(mod->getModInfo(), false);
@ -238,6 +240,8 @@ void ModCell::loadFromMod(Mod* mod) {
// } // }
this->updateState(); this->updateState();
return true;
} }
CCNode* ModCell::createLogo(CCSize const& size) { CCNode* ModCell::createLogo(CCSize const& size) {
@ -246,29 +250,34 @@ CCNode* ModCell::createLogo(CCSize const& size) {
// IndexItemCell // IndexItemCell
IndexItemCell::IndexItemCell(char const* name, CCSize const& size)
: ModListCell(name, size) {}
void IndexItemCell::onInfo(CCObject*) { void IndexItemCell::onInfo(CCObject*) {
IndexItemInfoPopup::create(m_item, m_list)->show(); IndexItemInfoPopup::create(m_item, m_layer)->show();
} }
IndexItemCell* IndexItemCell::create( IndexItemCell* IndexItemCell::create(
ModListView* list, ModListDisplay display, IndexItemHandle item,
const char* key, CCSize const& size ModListLayer* list,
CCSize const& size
) { ) {
auto ret = new IndexItemCell(key, size); auto ret = new IndexItemCell();
if (ret && ret->init(list, display)) { if (ret && ret->init(item, list, size)) {
return ret; return ret;
} }
CC_SAFE_DELETE(ret); CC_SAFE_DELETE(ret);
return nullptr; return nullptr;
} }
void IndexItemCell::loadFromItem(IndexItemHandle item) { bool IndexItemCell::init(
IndexItemHandle item,
ModListLayer* list,
CCSize const& size
) {
if (!ModListCell::init(list, size))
return false;
m_item = item; m_item = item;
this->setupInfo(item->info, true); this->setupInfo(item->info, item->tags.size());
auto viewSpr = ButtonSprite::create( auto viewSpr = ButtonSprite::create(
"View", "bigFont.fnt", "GJ_button_01.png", .8f "View", "bigFont.fnt", "GJ_button_01.png", .8f
@ -280,26 +289,28 @@ void IndexItemCell::loadFromItem(IndexItemHandle item) {
); );
m_menu->addChild(viewBtn); m_menu->addChild(viewBtn);
// if (hasCategories) { if (item->tags.size()) {
// float x = m_height / 2 + logoSize / 2 + 13.f; float x = m_height / 2 + this->getLogoSize() / 2 + 13.f;
// for (auto& category : modobj->m_index.m_categories) { for (auto& category : item->tags) {
// auto node = CategoryNode::create(category); auto node = TagNode::create(category);
// node->setAnchorPoint({ .0f, .5f }); node->setAnchorPoint({ .0f, .5f });
// node->setPositionX(x); node->setPositionX(x);
// node->setScale(.3f); node->setScale(.3f);
// if (hasDesc) { if (m_description) {
// node->setPositionY(m_height / 2 - 23.f); node->setPositionY(m_height / 2 - 23.f);
// } }
// else { else {
// node->setPositionY(m_height / 2 - 17.f); node->setPositionY(m_height / 2 - 12.f);
// } }
// m_mainLayer->addChild(node); this->addChild(node);
// x += node->getScaledContentSize().width + 5.f; x += node->getScaledContentSize().width + 5.f;
// } }
// } }
this->updateState(); this->updateState();
return true;
} }
void IndexItemCell::updateState() {} void IndexItemCell::updateState() {}
@ -310,9 +321,6 @@ CCNode* IndexItemCell::createLogo(CCSize const& size) {
// InvalidGeodeFileCell // InvalidGeodeFileCell
InvalidGeodeFileCell::InvalidGeodeFileCell(const char* name, CCSize const& size)
: ModListCell(name, size) {}
void InvalidGeodeFileCell::onInfo(CCObject*) { void InvalidGeodeFileCell::onInfo(CCObject*) {
FLAlertLayer::create( FLAlertLayer::create(
this, "Error Info", this, "Error Info",
@ -345,36 +353,29 @@ void InvalidGeodeFileCell::FLAlert_Clicked(FLAlertLayer*, bool btn2) {
)->show(); )->show();
} }
(void)Loader::get()->refreshModsList(); (void)Loader::get()->refreshModsList();
m_list->refreshList(); m_layer->reloadList();
} }
} }
InvalidGeodeFileCell* InvalidGeodeFileCell::create( bool InvalidGeodeFileCell::init(
ModListView* list, ModListDisplay display, InvalidGeodeFile const& info,
char const* key, CCSize const& size ModListLayer* list,
CCSize const& size
) { ) {
auto ret = new InvalidGeodeFileCell(key, size); if (!ModListCell::init(list, size))
if (ret && ret->init(list, display)) { return false;
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
void InvalidGeodeFileCell::loadFromInfo(InvalidGeodeFile const& info) {
m_info = info; m_info = info;
m_mainLayer->setVisible(true);
auto menu = CCMenu::create(); auto menu = CCMenu::create();
menu->setPosition(m_width - m_height, m_height / 2); menu->setPosition(m_width - m_height, m_height / 2);
m_mainLayer->addChild(menu); this->addChild(menu);
auto titleLabel = CCLabelBMFont::create("Failed to Load", "bigFont.fnt"); auto titleLabel = CCLabelBMFont::create("Failed to Load", "bigFont.fnt");
titleLabel->setAnchorPoint({ .0f, .5f }); titleLabel->setAnchorPoint({ .0f, .5f });
titleLabel->setScale(.5f); titleLabel->setScale(.5f);
titleLabel->setPosition(m_height / 2, m_height / 2 + 7.f); titleLabel->setPosition(m_height / 2, m_height / 2 + 7.f);
m_mainLayer->addChild(titleLabel); this->addChild(titleLabel);
auto pathLabel = CCLabelBMFont::create( auto pathLabel = CCLabelBMFont::create(
m_info.m_path.string().c_str(), m_info.m_path.string().c_str(),
@ -384,7 +385,7 @@ void InvalidGeodeFileCell::loadFromInfo(InvalidGeodeFile const& info) {
pathLabel->setScale(.43f); pathLabel->setScale(.43f);
pathLabel->setPosition(m_height / 2, m_height / 2 - 7.f); pathLabel->setPosition(m_height / 2, m_height / 2 - 7.f);
pathLabel->setColor({ 255, 255, 0 }); pathLabel->setColor({ 255, 255, 0 });
m_mainLayer->addChild(pathLabel); this->addChild(pathLabel);
auto whySpr = ButtonSprite::create( auto whySpr = ButtonSprite::create(
"Info", 0, 0, "bigFont.fnt", "GJ_button_01.png", 0, .8f "Info", 0, 0, "bigFont.fnt", "GJ_button_01.png", 0, .8f
@ -395,6 +396,22 @@ void InvalidGeodeFileCell::loadFromInfo(InvalidGeodeFile const& info) {
whySpr, this, menu_selector(InvalidGeodeFileCell::onInfo) whySpr, this, menu_selector(InvalidGeodeFileCell::onInfo)
); );
menu->addChild(viewBtn); menu->addChild(viewBtn);
return true;
}
InvalidGeodeFileCell* InvalidGeodeFileCell::create(
InvalidGeodeFile const& file,
ModListLayer* list,
CCSize const& size
) {
auto ret = new InvalidGeodeFileCell();
if (ret && ret->init(file, list, size)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
} }
void InvalidGeodeFileCell::updateState() {} void InvalidGeodeFileCell::updateState() {}

View file

@ -8,33 +8,45 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
class ModListView; class ModListLayer;
enum class ModListDisplay; enum class ModListDisplay;
class ModListCell : public TableViewCell { /**
* Base class for mod list items
*/
class ModListCell : public CCLayer {
protected: protected:
ModListView* m_list; float m_width;
float m_height;
ModListLayer* m_layer;
CCMenu* m_menu; CCMenu* m_menu;
CCLabelBMFont* m_description;
CCMenuItemToggler* m_enableToggle = nullptr; CCMenuItemToggler* m_enableToggle = nullptr;
CCMenuItemSpriteExtra* m_unresolvedExMark; CCMenuItemSpriteExtra* m_unresolvedExMark;
ModListDisplay m_display;
ModListCell(char const* name, CCSize const& size); bool init(ModListLayer* list, CCSize const& size);
bool init(ModListView* list, ModListDisplay display); void setupInfo(ModInfo const& info, bool spaceForTags);
void setupInfo(ModInfo const& info, bool spaceForCategories);
void draw() override; void draw() override;
float getLogoSize() const;
public: public:
void updateBGColor(int index);
virtual void updateState() = 0; virtual void updateState() = 0;
virtual CCNode* createLogo(CCSize const& size) = 0; virtual CCNode* createLogo(CCSize const& size) = 0;
}; };
/**
* Mod list item for a mod
*/
class ModCell : public ModListCell { class ModCell : public ModListCell {
protected: protected:
Mod* m_mod; Mod* m_mod;
ModCell(char const* name, CCSize const& size); bool init(
Mod* mod,
ModListLayer* list,
CCSize const& size
);
void onInfo(CCObject*); void onInfo(CCObject*);
void onEnable(CCObject*); void onEnable(CCObject*);
@ -42,50 +54,64 @@ protected:
public: public:
static ModCell* create( static ModCell* create(
ModListView* list, ModListDisplay display, Mod* mod,
const char* key, CCSize const& size ModListLayer* list,
CCSize const& size
); );
void loadFromMod(Mod* mod);
void updateState() override; void updateState() override;
CCNode* createLogo(CCSize const& size) override; CCNode* createLogo(CCSize const& size) override;
}; };
/**
* Mod list item for an index item
*/
class IndexItemCell : public ModListCell { class IndexItemCell : public ModListCell {
protected: protected:
IndexItemHandle m_item; IndexItemHandle m_item;
IndexItemCell(char const* name, CCSize const& size); bool init(
IndexItemHandle item,
ModListLayer* list,
CCSize const& size
);
void onInfo(CCObject*); void onInfo(CCObject*);
public: public:
static IndexItemCell* create( static IndexItemCell* create(
ModListView* list, ModListDisplay display, IndexItemHandle item,
const char* key, CCSize const& size ModListLayer* list,
CCSize const& size
); );
void loadFromItem(IndexItemHandle item);
void updateState() override; void updateState() override;
CCNode* createLogo(CCSize const& size) override; CCNode* createLogo(CCSize const& size) override;
}; };
/**
* Mod list item for an invalid Geode package
*/
class InvalidGeodeFileCell : public ModListCell, public FLAlertLayerProtocol { class InvalidGeodeFileCell : public ModListCell, public FLAlertLayerProtocol {
protected: protected:
InvalidGeodeFile m_info; InvalidGeodeFile m_info;
InvalidGeodeFileCell(char const* name, CCSize const& size); bool init(
InvalidGeodeFile const& file,
ModListLayer* list,
CCSize const& size
);
void onInfo(CCObject*); void onInfo(CCObject*);
void FLAlert_Clicked(FLAlertLayer*, bool btn2) override; void FLAlert_Clicked(FLAlertLayer*, bool btn2) override;
public: public:
static InvalidGeodeFileCell* create( static InvalidGeodeFileCell* create(
ModListView* list, ModListDisplay display, InvalidGeodeFile const& file,
const char* key, CCSize const& size ModListLayer* list,
CCSize const& size
); );
void loadFromInfo(InvalidGeodeFile const& file);
void updateState() override; void updateState() override;
CCNode* createLogo(CCSize const& size) override; CCNode* createLogo(CCSize const& size) override;
}; };

View file

@ -1,5 +1,5 @@
#include "ModListLayer.hpp" #include "ModListLayer.hpp"
#include "ModListCell.hpp"
#include "SearchFilterPopup.hpp" #include "SearchFilterPopup.hpp"
#include <Geode/binding/ButtonSprite.hpp> #include <Geode/binding/ButtonSprite.hpp>
@ -14,11 +14,42 @@
#include <Geode/ui/Notification.hpp> #include <Geode/ui/Notification.hpp>
#include <Geode/utils/casts.hpp> #include <Geode/utils/casts.hpp>
#include <Geode/loader/Dirs.hpp> #include <Geode/loader/Dirs.hpp>
#include <Geode/loader/Loader.hpp>
#include <optional> #include <optional>
#include <Geode/ui/ListView.hpp>
static ModListType g_tab = ModListType::Installed; static ModListType g_tab = ModListType::Installed;
static ModListLayer* g_instance = nullptr; static ModListLayer* g_instance = nullptr;
static void sortInstalledMods(std::vector<Mod*>& mods) {
if (!mods.size()) return;
// keep track of first object
size_t frontIndex = 0;
auto front = mods.front();
for (auto mod = mods.begin(); mod != mods.end(); mod++) {
// move mods with updates to front
if (auto item = Index::get()->getItem(*mod)) {
if (Index::get()->updateAvailable(item)) {
// swap first object and updatable mod
// if the updatable mod is the first object,
// nothing changes
std::rotate(mods.begin(), mod, mod + 1);
// get next object at front for next mod
// to sort
frontIndex++;
front = mods[frontIndex];
}
}
}
}
static std::vector<Mod*> sortedInstalledMods() {
auto mods = Loader::get()->getAllMods();
sortInstalledMods(mods);
return std::move(mods);
}
bool ModListLayer::init() { bool ModListLayer::init() {
if (!CCLayer::init()) return false; if (!CCLayer::init()) return false;
@ -216,11 +247,15 @@ void ModListLayer::reloadList() {
m_list->removeFromParent(); m_list->removeFromParent();
} }
auto items = ModListView::modsForType(g_tab); auto items = this->createModCells(g_tab);
// create new list // create new list
auto list = ModListView::create(items, m_display); auto list = ListView::create(
list->setLayer(this); items,
this->getCellSize().height,
this->getListSize().width,
this->getListSize().height
);
// set list status // set list status
if (!items->count()) { if (!items->count()) {
@ -306,6 +341,68 @@ void ModListLayer::reloadList() {
} }
} }
CCSize ModListLayer::getListSize() const {
return { 358.f, 190.f };
}
CCSize ModListLayer::getCellSize() const {
return {
getListSize().width,
m_display == ModListDisplay::Expanded ? 60.f : 40.f
};
}
ModListDisplay ModListLayer::getDisplay() const {
return m_display;
}
CCArray* ModListLayer::createModCells(ModListType type) {
auto mods = CCArray::create();
switch (type) {
default:
case ModListType::Installed: {
// failed mods first
for (auto const& mod : Loader::get()->getFailedMods()) {
mods->addObject(InvalidGeodeFileCell::create(mod, this, this->getCellSize()));
}
// internal geode representation always at the top
auto imod = Loader::getInternalMod();
mods->addObject(ModCell::create(imod, this, this->getCellSize()));
// then other mods
for (auto const& mod : sortedInstalledMods()) {
// if the mod is no longer installed nor
// loaded, it's as good as not existing
// (because it doesn't)
if (mod->isUninstalled() && !mod->isLoaded()) continue;
mods->addObject(ModCell::create(mod, this, this->getCellSize()));
}
} break;
case ModListType::Download: {
for (auto const& item : Index::get()->getItems()) {
mods->addObject(IndexItemCell::create(item, this, this->getCellSize()));
}
} break;
case ModListType::Featured: {
// todo: featured
} break;
}
return mods;
}
void ModListLayer::updateAllStates(ModListCell* toggled) {
for (auto cell : CCArrayExt<GenericListCell>(
m_list->m_listView->m_tableView->m_cellArray
)) {
auto node = static_cast<ModListCell*>(cell->getChildByID("mod-list-cell"));
if (toggled != node) {
node->updateState();
}
}
}
void ModListLayer::onCheckForUpdates(CCObject*) { void ModListLayer::onCheckForUpdates(CCObject*) {
// store instance in a global so the // store instance in a global so the
// layer stays in memory even if the // layer stays in memory even if the
@ -331,10 +428,6 @@ void ModListLayer::onIndexUpdate(IndexUpdateEvent* event) {
}, event->status); }, event->status);
} }
void ModListLayer::textChanged(CCTextInputNode* input) {
this->reloadList();
}
void ModListLayer::onExit(CCObject*) { void ModListLayer::onExit(CCObject*) {
CCDirector::sharedDirector()->replaceScene( CCDirector::sharedDirector()->replaceScene(
CCTransitionFade::create(.5f, MenuLayer::scene(false)) CCTransitionFade::create(.5f, MenuLayer::scene(false))
@ -365,12 +458,6 @@ void ModListLayer::onResetSearch(CCObject*) {
m_searchInput->setString(""); m_searchInput->setString("");
} }
void ModListLayer::keyDown(enumKeyCodes key) {
if (key == KEY_Escape) {
this->onExit(nullptr);
}
}
void ModListLayer::onTab(CCObject* pSender) { void ModListLayer::onTab(CCObject* pSender) {
if (pSender) { if (pSender) {
g_tab = static_cast<ModListType>(pSender->getTag()); g_tab = static_cast<ModListType>(pSender->getTag());
@ -394,6 +481,16 @@ void ModListLayer::onTab(CCObject* pSender) {
toggleTab(m_featuredTabBtn); toggleTab(m_featuredTabBtn);
} }
void ModListLayer::keyDown(enumKeyCodes key) {
if (key == KEY_Escape) {
this->onExit(nullptr);
}
}
void ModListLayer::textChanged(CCTextInputNode* input) {
this->reloadList();
}
ModListLayer* ModListLayer::create() { ModListLayer* ModListLayer::create() {
// return global instance if one exists // return global instance if one exists
if (g_instance) return g_instance; if (g_instance) return g_instance;

View file

@ -1,13 +1,23 @@
#pragma once #pragma once
#include "ModListView.hpp"
#include <Geode/binding/TextInputDelegate.hpp> #include <Geode/binding/TextInputDelegate.hpp>
#include <Geode/loader/Index.hpp> #include <Geode/loader/Index.hpp>
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
class SearchFilterPopup; class SearchFilterPopup;
class ModListCell;
enum class ModListType {
Installed,
Download,
Featured,
};
enum class ModListDisplay {
Concise,
Expanded,
};
class ModListLayer : public CCLayer, public TextInputDelegate { class ModListLayer : public CCLayer, public TextInputDelegate {
protected: protected:
@ -46,11 +56,18 @@ protected:
void createSearchControl(); void createSearchControl();
void onIndexUpdate(IndexUpdateEvent* event); void onIndexUpdate(IndexUpdateEvent* event);
CCArray* createModCells(ModListType type);
CCSize getCellSize() const;
CCSize getListSize() const;
friend class SearchFilterPopup; friend class SearchFilterPopup;
public: public:
static ModListLayer* create(); static ModListLayer* create();
static ModListLayer* scene(); static ModListLayer* scene();
void updateAllStates(ModListCell* except = nullptr);
ModListDisplay getDisplay() const;
void reloadList(); void reloadList();
}; };

View file

@ -1,164 +0,0 @@
#include "ModListView.hpp"
#include "../info/CategoryNode.hpp"
#include "ModListLayer.hpp"
#include "ModListCell.hpp"
#include <Geode/binding/ButtonSprite.hpp>
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/TableView.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
#include <Geode/binding/CCContentLayer.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/utils/casts.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/loader/Index.hpp>
#include <InternalLoader.hpp>
void ModListView::updateAllStates(ModListCell* toggled) {
for (auto cell : CCArrayExt<ModListCell>(m_tableView->m_cellArray)) {
if (toggled != cell) {
cell->updateState();
}
}
}
void ModListView::setupList() {
m_itemSeparation = m_display == ModListDisplay::Expanded ? 60.f : 40.0f;
if (!m_entries->count()) return;
m_tableView->reloadData();
// fix content layer content size so the
// list is properly aligned to the top
auto coverage = calculateChildCoverage(m_tableView->m_contentLayer);
m_tableView->m_contentLayer->setContentSize({
-coverage.origin.x + coverage.size.width,
-coverage.origin.y + coverage.size.height
});
if (m_entries->count() == 1) {
m_tableView->moveToTopWithOffset(m_itemSeparation * 2);
}
else if (m_entries->count() == 2) {
m_tableView->moveToTopWithOffset(-m_itemSeparation);
}
else {
m_tableView->moveToTop();
}
}
TableViewCell* ModListView::getListCell(char const* key) {
return ModCell::create(this, m_display, key, { m_width, m_itemSeparation });
}
void ModListView::loadCell(TableViewCell* cell, unsigned int index) {
auto obj = m_entries->objectAtIndex(index);
if (auto mod = typeinfo_cast<ModObject*>(obj)) {
as<ModCell*>(cell)->loadFromMod(mod->mod);
}
if (auto mod = typeinfo_cast<IndexItemObject*>(obj)) {
// as<IndexItemCell*>(cell)->loadFromItem(mod->item);
}
if (auto failed = typeinfo_cast<InvalidGeodeFileObject*>(obj)) {
as<InvalidGeodeFileCell*>(cell)->loadFromInfo(failed->info);
}
as<ModListCell*>(cell)->updateBGColor(index);
}
static void sortInstalledMods(std::vector<Mod*>& mods) {
if (!mods.size()) return;
// keep track of first object
size_t frontIndex = 0;
auto front = mods.front();
for (auto mod = mods.begin(); mod != mods.end(); mod++) {
// move mods with updates to front
if (auto item = Index::get()->getItem(*mod)) {
if (Index::get()->updateAvailable(item)) {
// swap first object and updatable mod
// if the updatable mod is the first object,
// nothing changes
std::rotate(mods.begin(), mod, mod + 1);
// get next object at front for next mod
// to sort
frontIndex++;
front = mods[frontIndex];
}
}
}
}
static std::vector<Mod*> sortedInstalledMods() {
auto mods = Loader::get()->getAllMods();
sortInstalledMods(mods);
return std::move(mods);
}
bool ModListView::init(CCArray* mods, ModListDisplay display) {
m_display = display;
return CustomListView::init(mods, BoomListType::Default, 358.f, 190.f);
}
CCArray* ModListView::modsForType(ModListType type) {
auto mods = CCArray::create();
switch (type) {
default:
case ModListType::Installed: {
// failed mods first
for (auto const& mod : Loader::get()->getFailedMods()) {
mods->addObject(new InvalidGeodeFileObject(mod));
}
// internal geode representation always at the top
auto imod = Loader::getInternalMod();
mods->addObject(new ModObject(imod));
// then other mods
for (auto const& mod : sortedInstalledMods()) {
// if the mod is no longer installed nor
// loaded, it's as good as not existing
// (because it doesn't)
if (mod->isUninstalled() && !mod->isLoaded()) continue;
mods->addObject(new ModObject(mod));
}
} break;
case ModListType::Download: {
for (auto const& item : Index::get()->getItems()) {
mods->addObject(new IndexItemObject(item));
}
} break;
case ModListType::Featured: {
} break;
}
return mods;
}
ModListView* ModListView::create(CCArray* mods, ModListDisplay display) {
auto pRet = new ModListView;
if (pRet) {
if (pRet->init(mods, display)) {
pRet->autorelease();
return pRet;
}
}
CC_SAFE_DELETE(pRet);
return nullptr;
}
ModListView* ModListView::create(ModListType type, ModListDisplay display) {
return ModListView::create(modsForType(type), display);
}
void ModListView::setLayer(ModListLayer* layer) {
m_layer = layer;
}
void ModListView::refreshList() {
if (m_layer) {
m_layer->reloadList();
}
}

View file

@ -1,67 +0,0 @@
#pragma once
#include <Geode/binding/CustomListView.hpp>
#include <Geode/binding/FLAlertLayerProtocol.hpp>
#include <Geode/binding/TableViewCell.hpp>
#include <Geode/loader/Index.hpp>
#include <Geode/loader/Loader.hpp>
#include <optional>
USE_GEODE_NAMESPACE();
enum class ModListType {
Installed,
Download,
Featured,
};
enum class ModListDisplay {
Concise,
Expanded,
};
class ModListLayer;
class ModListCell;
// for passing invalid files as CCObject
struct InvalidGeodeFileObject : public CCObject {
InvalidGeodeFile info;
inline InvalidGeodeFileObject(InvalidGeodeFile const& info) : info(info) {
this->autorelease();
}
};
struct ModObject : public CCObject {
Mod* mod;
inline ModObject(Mod* mod) : mod(mod) {
this->autorelease();
}
};
struct IndexItemObject : public CCObject {
IndexItemHandle item;
inline IndexItemObject(IndexItemHandle item) : item(item) {
this->autorelease();
}
};
class ModListView : public CustomListView {
protected:
ModListLayer* m_layer = nullptr;
ModListDisplay m_display;
void setupList() override;
TableViewCell* getListCell(char const* key) override;
void loadCell(TableViewCell* cell, unsigned int index) override;
bool init(CCArray* mods, ModListDisplay display);
public:
static ModListView* create(CCArray* mods, ModListDisplay display);
static ModListView* create(ModListType type, ModListDisplay display);
static CCArray* modsForType(ModListType type);
void updateAllStates(ModListCell* except = nullptr);
void setLayer(ModListLayer* layer);
void refreshList();
};

View file

@ -1,8 +1,7 @@
#include "SearchFilterPopup.hpp" #include "SearchFilterPopup.hpp"
#include "../info/CategoryNode.hpp" #include "../info/TagNode.hpp"
#include "ModListLayer.hpp" #include "ModListLayer.hpp"
#include "ModListView.hpp"
#include <Geode/binding/GameToolbox.hpp> #include <Geode/binding/GameToolbox.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp> #include <Geode/binding/CCMenuItemToggler.hpp>