diff --git a/loader/include/Geode/loader/Index.hpp b/loader/include/Geode/loader/Index.hpp index 6acc72d0..005c7757 100644 --- a/loader/include/Geode/loader/Index.hpp +++ b/loader/include/Geode/loader/Index.hpp @@ -58,6 +58,7 @@ namespace geode { std::unordered_set platforms; } download; bool isFeatured; + std::unordered_set tags; /** * Create IndexItem from a directory diff --git a/loader/src/loader/Index.cpp b/loader/src/loader/Index.cpp index cfaf7175..7cf7cf66 100644 --- a/loader/src/loader/Index.cpp +++ b/loader/src/loader/Index.cpp @@ -122,6 +122,7 @@ Result IndexItem::createFromDir( .platforms = platforms, }, .isFeatured = root.has("is-featured").template get(), + .tags = root.has("tags").template get>() }); if (checker.isError()) { return Err(checker.getError()); diff --git a/loader/src/ui/internal/info/CategoryNode.hpp b/loader/src/ui/internal/info/CategoryNode.hpp deleted file mode 100644 index 9ca1173e..00000000 --- a/loader/src/ui/internal/info/CategoryNode.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -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); -}; diff --git a/loader/src/ui/internal/info/ModInfoPopup.cpp b/loader/src/ui/internal/info/ModInfoPopup.cpp index 50ce37d7..cd8daa61 100644 --- a/loader/src/ui/internal/info/ModInfoPopup.cpp +++ b/loader/src/ui/internal/info/ModInfoPopup.cpp @@ -1,7 +1,7 @@ #include "ModInfoPopup.hpp" #include "../dev/HookListLayer.hpp" -#include "../list/ModListView.hpp" +#include "../list/ModListLayer.hpp" #include "../settings/ModSettingsPopup.hpp" #include "../settings/AdvancedSettingsPopup.hpp" #include @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -30,9 +31,9 @@ static constexpr int const TAG_CONFIRM_UNINSTALL = 5; static constexpr int const TAG_DELETE_SAVEDATA = 6; 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_list = list; + m_layer = list; auto winSize = CCDirector::sharedDirector()->getWinSize(); @@ -274,7 +275,7 @@ void ModInfoPopup::onClose(CCObject* pSender) { // LocalModInfoPopup -bool LocalModInfoPopup::init(Mod* mod, ModListView* list) { +bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) { m_mod = mod; if (!ModInfoPopup::init(mod->getModInfo(), list)) @@ -458,7 +459,7 @@ void LocalModInfoPopup::onEnableMod(CCObject* sender) { "OK" ) ->show(); - if (m_list) m_list->updateAllStates(nullptr); + if (m_layer) m_layer->updateAllStates(nullptr); return; } if (as(sender)->isToggled()) { @@ -479,7 +480,7 @@ void LocalModInfoPopup::onEnableMod(CCObject* sender) { )->show(); } } - if (m_list) m_list->updateAllStates(nullptr); + if (m_layer) m_layer->updateAllStates(nullptr); as(sender)->toggle(m_mod->isEnabled()); } @@ -530,7 +531,7 @@ void LocalModInfoPopup::FLAlert_Clicked(FLAlertLayer* layer, bool btn2) { )->show(); } } - if (m_list) m_list->refreshList(); + if (m_layer) m_layer->reloadList(); this->onClose(nullptr); } break; } @@ -556,7 +557,7 @@ void LocalModInfoPopup::uninstall() { layer->show(); } -LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListView* list) { +LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListLayer* list) { auto ret = new LocalModInfoPopup; if (ret && ret->init(mod, list)) { ret->autorelease(); @@ -568,7 +569,7 @@ LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListView* list) { // IndexItemInfoPopup -bool IndexItemInfoPopup::init(IndexItemHandle item, ModListView* list) { +bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) { m_item = item; auto winSize = CCDirector::sharedDirector()->getWinSize(); @@ -608,7 +609,7 @@ ModInfo IndexItemInfoPopup::getModInfo() const { } IndexItemInfoPopup* IndexItemInfoPopup::create( - IndexItemHandle item, ModListView* list + IndexItemHandle item, ModListLayer* list ) { auto ret = new IndexItemInfoPopup; if (ret && ret->init(item, list)) { diff --git a/loader/src/ui/internal/info/ModInfoPopup.hpp b/loader/src/ui/internal/info/ModInfoPopup.hpp index c47a332a..a258357a 100644 --- a/loader/src/ui/internal/info/ModInfoPopup.hpp +++ b/loader/src/ui/internal/info/ModInfoPopup.hpp @@ -9,7 +9,7 @@ USE_GEODE_NAMESPACE(); -class ModListView; +class ModListLayer; class ModObject; class DownloadStatusNode : public CCNode { @@ -28,7 +28,7 @@ public: class ModInfoPopup : public FLAlertLayer { protected: - ModListView* m_list = nullptr; + ModListLayer* m_layer = nullptr; DownloadStatusNode* m_installStatus = nullptr; IconButtonSprite* m_installBtnSpr; CCMenuItemSpriteExtra* m_installBtn; @@ -43,7 +43,7 @@ protected: void onSupport(CCObject*); void onInfo(CCObject*); - bool init(ModInfo const& info, ModListView* list); + bool init(ModInfo const& info, ModListLayer* list); void keyDown(cocos2d::enumKeyCodes) override; void onClose(cocos2d::CCObject*); @@ -56,7 +56,7 @@ class LocalModInfoPopup : public ModInfoPopup, public FLAlertLayerProtocol { protected: Mod* m_mod; - bool init(Mod* mod, ModListView* list); + bool init(Mod* mod, ModListLayer* list); void onIssues(CCObject*); void onSettings(CCObject*); @@ -74,18 +74,18 @@ protected: ModInfo getModInfo() const override; public: - static LocalModInfoPopup* create(Mod* mod, ModListView* list); + static LocalModInfoPopup* create(Mod* mod, ModListLayer* list); }; class IndexItemInfoPopup : public ModInfoPopup { protected: IndexItemHandle m_item; - bool init(IndexItemHandle item, ModListView* list); + bool init(IndexItemHandle item, ModListLayer* list); CCNode* createLogo(CCSize const& size) override; ModInfo getModInfo() const override; public: - static IndexItemInfoPopup* create(IndexItemHandle item, ModListView* list); + static IndexItemInfoPopup* create(IndexItemHandle item, ModListLayer* list); }; diff --git a/loader/src/ui/internal/info/CategoryNode.cpp b/loader/src/ui/internal/info/TagNode.cpp similarity index 80% rename from loader/src/ui/internal/info/CategoryNode.cpp rename to loader/src/ui/internal/info/TagNode.cpp index 82c74372..ec2ffba4 100644 --- a/loader/src/ui/internal/info/CategoryNode.cpp +++ b/loader/src/ui/internal/info/TagNode.cpp @@ -1,6 +1,9 @@ -#include "CategoryNode.hpp" +#include "TagNode.hpp" +#include +#include +#include -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 // between 0 and 360 and for that number to always be the // same for the same string @@ -17,8 +20,8 @@ ccColor3B CategoryNode::categoryToColor(std::string const& category) { static_cast(rgb.b * 255) }; } -bool CategoryNode::init(std::string const& category, CategoryNodeStyle style) { - if (style == CategoryNodeStyle::Dot) { +bool TagNode::init(std::string const& category, TagNodeStyle style) { + if (style == TagNodeStyle::Dot) { auto dot = CCSprite::createWithSpriteFrameName("category-dot.png"_spr); dot->setColor(categoryToColor(category)); dot->setPosition({ 20.f, 20.f }); @@ -52,8 +55,8 @@ bool CategoryNode::init(std::string const& category, CategoryNodeStyle style) { return true; } -CategoryNode* CategoryNode::create(std::string const& category, CategoryNodeStyle style) { - auto ret = new CategoryNode(); +TagNode* TagNode::create(std::string const& category, TagNodeStyle style) { + auto ret = new TagNode(); if (ret && ret->init(category, style)) { ret->autorelease(); return ret; diff --git a/loader/src/ui/internal/info/TagNode.hpp b/loader/src/ui/internal/info/TagNode.hpp new file mode 100644 index 00000000..7e762b9c --- /dev/null +++ b/loader/src/ui/internal/info/TagNode.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +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); +}; diff --git a/loader/src/ui/internal/list/ModListCell.cpp b/loader/src/ui/internal/list/ModListCell.cpp index d5e58030..86e90656 100644 --- a/loader/src/ui/internal/list/ModListCell.cpp +++ b/loader/src/ui/internal/list/ModListCell.cpp @@ -1,5 +1,5 @@ #include "ModListCell.hpp" -#include "ModListView.hpp" +#include "ModListLayer.hpp" #include "../info/ModInfoPopup.hpp" #include #include @@ -8,6 +8,7 @@ #include #include #include +#include "../info/TagNode.hpp" template static bool tryOrAlert(Result const& res, char const* title) { @@ -17,45 +18,46 @@ static bool tryOrAlert(Result const& res, char const* title) { return res.isOk(); } -ModListCell::ModListCell(char const* name, CCSize const& size) - : TableViewCell(name, size.width, size.height) {} - void ModListCell::draw() { reinterpret_cast(this)->StatsCell::draw(); } -void ModListCell::setupInfo(ModInfo const& info, bool spaceForCategories) { - m_mainLayer->setVisible(true); - m_backgroundLayer->setOpacity(255); +float ModListCell::getLogoSize() const { + return m_height / 1.5f; +} +void ModListCell::setupInfo(ModInfo const& info, bool spaceForTags) { m_menu = CCMenu::create(); 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 }); logoSpr->setPosition({ logoSize / 2 + 12.f, m_height / 2 }); - m_mainLayer->addChild(logoSpr); + this->addChild(logoSpr); bool hasDesc = - m_display == ModListDisplay::Expanded && + m_layer->getDisplay() == ModListDisplay::Expanded && info.m_description.has_value(); auto titleLabel = CCLabelBMFont::create(info.m_name.c_str(), "bigFont.fnt"); titleLabel->setAnchorPoint({ .0f, .5f }); titleLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f); - if (hasDesc && spaceForCategories) { + if (hasDesc && spaceForTags) { 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); } else { titleLabel->setPositionY(m_height / 2 + 7.f); } 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"); versionLabel->setAnchorPoint({ .0f, .5f }); @@ -65,23 +67,23 @@ void ModListCell::setupInfo(ModInfo const& info, bool spaceForCategories) { titleLabel->getPositionY() - 1.f ); versionLabel->setColor({ 0, 255, 0 }); - m_mainLayer->addChild(versionLabel); + this->addChild(versionLabel); auto creatorStr = "by " + info.m_developer; auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt"); creatorLabel->setAnchorPoint({ .0f, .5f }); creatorLabel->setScale(.43f); creatorLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f); - if (hasDesc && spaceForCategories) { + if (hasDesc && spaceForTags) { creatorLabel->setPositionY(m_height / 2 + 7.5f); } - else if (hasDesc || spaceForCategories) { + else if (hasDesc || spaceForTags) { creatorLabel->setPositionY(m_height / 2); } else { creatorLabel->setPositionY(m_height / 2 - 7.f); } - m_mainLayer->addChild(creatorLabel); + this->addChild(creatorLabel); if (hasDesc) { 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->setAnchorPoint({ .0f, .5f }); descBG->setPositionX(m_height / 2 + logoSize / 2 + 13.f); - if (spaceForCategories) { + if (spaceForTags) { descBG->setPositionY(m_height / 2 - 7.5f); } else { descBG->setPositionY(m_height / 2 - 17.f); } descBG->setScale(.25f); - m_mainLayer->addChild(descBG); + this->addChild(descBG); - auto descText = CCLabelBMFont::create(info.m_description.value().c_str(), "chatFont.fnt"); - descText->setAnchorPoint({ .0f, .5f }); - descText->setPosition(m_height / 2 + logoSize / 2 + 18.f, descBG->getPositionY()); - descText->limitLabelWidth(m_width / 2 - 10.f, .5f, .1f); - m_mainLayer->addChild(descText); + m_description = CCLabelBMFont::create(info.m_description.value().c_str(), "chatFont.fnt"); + m_description->setAnchorPoint({ .0f, .5f }); + m_description->setPosition(m_height / 2 + logoSize / 2 + 18.f, descBG->getPositionY()); + m_description->limitLabelWidth(m_width / 2 - 10.f, .5f, .1f); + this->addChild(m_description); } } -void ModListCell::updateBGColor(int index) { - if (index % 2) { - m_backgroundLayer->setColor(ccc3(0xc2, 0x72, 0x3e)); - } - else m_backgroundLayer->setColor(ccc3(0xa1, 0x58, 0x2c)); - m_backgroundLayer->setOpacity(0xff); -} - -bool ModListCell::init(ModListView* list, ModListDisplay display) { - m_list = list; - m_display = display; +bool ModListCell::init(ModListLayer* list, CCSize const& size) { + m_width = size.width; + m_height = size.height; + m_layer = list; + this->setContentSize(size); + this->setID("mod-list-cell"); return true; } // ModCell -ModCell::ModCell(const char* name, CCSize const& size) - : ModListCell(name, size) {} - ModCell* ModCell::create( - ModListView* list, ModListDisplay display, - const char* key, CCSize const& size + Mod* mod, + ModListLayer* list, + CCSize const& size ) { - auto ret = new ModCell(key, size); - if (ret && ret->init(list, display)) { + auto ret = new ModCell(); + if (ret && ret->init(mod, list, size)) { return ret; } CC_SAFE_DELETE(ret); @@ -148,7 +143,7 @@ void ModCell::onEnable(CCObject* sender) { "need to restart the game to have it fully unloaded.", "OK" )->show(); - m_list->updateAllStates(this); + m_layer->updateAllStates(this); return; } if (!as(sender)->isToggled()) { @@ -157,7 +152,7 @@ void ModCell::onEnable(CCObject* sender) { else { tryOrAlert(m_mod->disable(), "Error disabling mod"); } - m_list->updateAllStates(this); + m_layer->updateAllStates(this); } void ModCell::onUnresolvedInfo(CCObject*) { @@ -176,7 +171,7 @@ void ModCell::onUnresolvedInfo(CCObject*) { } void ModCell::onInfo(CCObject*) { - LocalModInfoPopup::create(m_mod, m_list)->show(); + LocalModInfoPopup::create(m_mod, m_layer)->show(); } void ModCell::updateState() { @@ -192,7 +187,14 @@ void ModCell::updateState() { 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; this->setupInfo(mod->getModInfo(), false); @@ -238,6 +240,8 @@ void ModCell::loadFromMod(Mod* mod) { // } this->updateState(); + + return true; } CCNode* ModCell::createLogo(CCSize const& size) { @@ -246,29 +250,34 @@ CCNode* ModCell::createLogo(CCSize const& size) { // IndexItemCell -IndexItemCell::IndexItemCell(char const* name, CCSize const& size) - : ModListCell(name, size) {} - void IndexItemCell::onInfo(CCObject*) { - IndexItemInfoPopup::create(m_item, m_list)->show(); + IndexItemInfoPopup::create(m_item, m_layer)->show(); } IndexItemCell* IndexItemCell::create( - ModListView* list, ModListDisplay display, - const char* key, CCSize const& size + IndexItemHandle item, + ModListLayer* list, + CCSize const& size ) { - auto ret = new IndexItemCell(key, size); - if (ret && ret->init(list, display)) { + auto ret = new IndexItemCell(); + if (ret && ret->init(item, list, size)) { return ret; } CC_SAFE_DELETE(ret); 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; - this->setupInfo(item->info, true); + this->setupInfo(item->info, item->tags.size()); auto viewSpr = ButtonSprite::create( "View", "bigFont.fnt", "GJ_button_01.png", .8f @@ -280,26 +289,28 @@ void IndexItemCell::loadFromItem(IndexItemHandle item) { ); m_menu->addChild(viewBtn); - // if (hasCategories) { - // float x = m_height / 2 + logoSize / 2 + 13.f; - // for (auto& category : modobj->m_index.m_categories) { - // auto node = CategoryNode::create(category); - // node->setAnchorPoint({ .0f, .5f }); - // node->setPositionX(x); - // node->setScale(.3f); - // if (hasDesc) { - // node->setPositionY(m_height / 2 - 23.f); - // } - // else { - // node->setPositionY(m_height / 2 - 17.f); - // } - // m_mainLayer->addChild(node); + if (item->tags.size()) { + float x = m_height / 2 + this->getLogoSize() / 2 + 13.f; + for (auto& category : item->tags) { + auto node = TagNode::create(category); + node->setAnchorPoint({ .0f, .5f }); + node->setPositionX(x); + node->setScale(.3f); + if (m_description) { + node->setPositionY(m_height / 2 - 23.f); + } + else { + node->setPositionY(m_height / 2 - 12.f); + } + this->addChild(node); - // x += node->getScaledContentSize().width + 5.f; - // } - // } + x += node->getScaledContentSize().width + 5.f; + } + } this->updateState(); + + return true; } void IndexItemCell::updateState() {} @@ -310,9 +321,6 @@ CCNode* IndexItemCell::createLogo(CCSize const& size) { // InvalidGeodeFileCell -InvalidGeodeFileCell::InvalidGeodeFileCell(const char* name, CCSize const& size) - : ModListCell(name, size) {} - void InvalidGeodeFileCell::onInfo(CCObject*) { FLAlertLayer::create( this, "Error Info", @@ -345,36 +353,29 @@ void InvalidGeodeFileCell::FLAlert_Clicked(FLAlertLayer*, bool btn2) { )->show(); } (void)Loader::get()->refreshModsList(); - m_list->refreshList(); + m_layer->reloadList(); } } -InvalidGeodeFileCell* InvalidGeodeFileCell::create( - ModListView* list, ModListDisplay display, - char const* key, CCSize const& size +bool InvalidGeodeFileCell::init( + InvalidGeodeFile const& info, + ModListLayer* list, + CCSize const& size ) { - auto ret = new InvalidGeodeFileCell(key, size); - if (ret && ret->init(list, display)) { - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; -} + if (!ModListCell::init(list, size)) + return false; -void InvalidGeodeFileCell::loadFromInfo(InvalidGeodeFile const& info) { m_info = info; - m_mainLayer->setVisible(true); - auto menu = CCMenu::create(); 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"); titleLabel->setAnchorPoint({ .0f, .5f }); titleLabel->setScale(.5f); titleLabel->setPosition(m_height / 2, m_height / 2 + 7.f); - m_mainLayer->addChild(titleLabel); + this->addChild(titleLabel); auto pathLabel = CCLabelBMFont::create( m_info.m_path.string().c_str(), @@ -384,7 +385,7 @@ void InvalidGeodeFileCell::loadFromInfo(InvalidGeodeFile const& info) { pathLabel->setScale(.43f); pathLabel->setPosition(m_height / 2, m_height / 2 - 7.f); pathLabel->setColor({ 255, 255, 0 }); - m_mainLayer->addChild(pathLabel); + this->addChild(pathLabel); auto whySpr = ButtonSprite::create( "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) ); 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() {} diff --git a/loader/src/ui/internal/list/ModListCell.hpp b/loader/src/ui/internal/list/ModListCell.hpp index 1212011b..e96d06ba 100644 --- a/loader/src/ui/internal/list/ModListCell.hpp +++ b/loader/src/ui/internal/list/ModListCell.hpp @@ -8,33 +8,45 @@ USE_GEODE_NAMESPACE(); -class ModListView; +class ModListLayer; enum class ModListDisplay; -class ModListCell : public TableViewCell { +/** + * Base class for mod list items + */ +class ModListCell : public CCLayer { protected: - ModListView* m_list; + float m_width; + float m_height; + ModListLayer* m_layer; CCMenu* m_menu; + CCLabelBMFont* m_description; CCMenuItemToggler* m_enableToggle = nullptr; CCMenuItemSpriteExtra* m_unresolvedExMark; - ModListDisplay m_display; - ModListCell(char const* name, CCSize const& size); - bool init(ModListView* list, ModListDisplay display); - void setupInfo(ModInfo const& info, bool spaceForCategories); + bool init(ModListLayer* list, CCSize const& size); + void setupInfo(ModInfo const& info, bool spaceForTags); void draw() override; + float getLogoSize() const; + public: - void updateBGColor(int index); virtual void updateState() = 0; virtual CCNode* createLogo(CCSize const& size) = 0; }; +/** + * Mod list item for a mod + */ class ModCell : public ModListCell { protected: Mod* m_mod; - ModCell(char const* name, CCSize const& size); + bool init( + Mod* mod, + ModListLayer* list, + CCSize const& size + ); void onInfo(CCObject*); void onEnable(CCObject*); @@ -42,50 +54,64 @@ protected: public: static ModCell* create( - ModListView* list, ModListDisplay display, - const char* key, CCSize const& size + Mod* mod, + ModListLayer* list, + CCSize const& size ); - void loadFromMod(Mod* mod); void updateState() override; CCNode* createLogo(CCSize const& size) override; }; +/** + * Mod list item for an index item + */ class IndexItemCell : public ModListCell { protected: IndexItemHandle m_item; - IndexItemCell(char const* name, CCSize const& size); + bool init( + IndexItemHandle item, + ModListLayer* list, + CCSize const& size + ); void onInfo(CCObject*); public: static IndexItemCell* create( - ModListView* list, ModListDisplay display, - const char* key, CCSize const& size + IndexItemHandle item, + ModListLayer* list, + CCSize const& size ); - void loadFromItem(IndexItemHandle item); void updateState() override; CCNode* createLogo(CCSize const& size) override; }; +/** + * Mod list item for an invalid Geode package + */ class InvalidGeodeFileCell : public ModListCell, public FLAlertLayerProtocol { protected: InvalidGeodeFile m_info; - InvalidGeodeFileCell(char const* name, CCSize const& size); + bool init( + InvalidGeodeFile const& file, + ModListLayer* list, + CCSize const& size + ); void onInfo(CCObject*); void FLAlert_Clicked(FLAlertLayer*, bool btn2) override; public: static InvalidGeodeFileCell* create( - ModListView* list, ModListDisplay display, - const char* key, CCSize const& size + InvalidGeodeFile const& file, + ModListLayer* list, + CCSize const& size ); - void loadFromInfo(InvalidGeodeFile const& file); void updateState() override; CCNode* createLogo(CCSize const& size) override; }; diff --git a/loader/src/ui/internal/list/ModListLayer.cpp b/loader/src/ui/internal/list/ModListLayer.cpp index af1cfab2..11dbbceb 100644 --- a/loader/src/ui/internal/list/ModListLayer.cpp +++ b/loader/src/ui/internal/list/ModListLayer.cpp @@ -1,5 +1,5 @@ #include "ModListLayer.hpp" - +#include "ModListCell.hpp" #include "SearchFilterPopup.hpp" #include @@ -14,11 +14,42 @@ #include #include #include +#include #include +#include static ModListType g_tab = ModListType::Installed; static ModListLayer* g_instance = nullptr; +static void sortInstalledMods(std::vector& 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 sortedInstalledMods() { + auto mods = Loader::get()->getAllMods(); + sortInstalledMods(mods); + return std::move(mods); +} + bool ModListLayer::init() { if (!CCLayer::init()) return false; @@ -216,11 +247,15 @@ void ModListLayer::reloadList() { m_list->removeFromParent(); } - auto items = ModListView::modsForType(g_tab); + auto items = this->createModCells(g_tab); // create new list - auto list = ModListView::create(items, m_display); - list->setLayer(this); + auto list = ListView::create( + items, + this->getCellSize().height, + this->getListSize().width, + this->getListSize().height + ); // set list status 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( + m_list->m_listView->m_tableView->m_cellArray + )) { + auto node = static_cast(cell->getChildByID("mod-list-cell")); + if (toggled != node) { + node->updateState(); + } + } +} + void ModListLayer::onCheckForUpdates(CCObject*) { // store instance in a global so the // layer stays in memory even if the @@ -331,10 +428,6 @@ void ModListLayer::onIndexUpdate(IndexUpdateEvent* event) { }, event->status); } -void ModListLayer::textChanged(CCTextInputNode* input) { - this->reloadList(); -} - void ModListLayer::onExit(CCObject*) { CCDirector::sharedDirector()->replaceScene( CCTransitionFade::create(.5f, MenuLayer::scene(false)) @@ -365,12 +458,6 @@ void ModListLayer::onResetSearch(CCObject*) { m_searchInput->setString(""); } -void ModListLayer::keyDown(enumKeyCodes key) { - if (key == KEY_Escape) { - this->onExit(nullptr); - } -} - void ModListLayer::onTab(CCObject* pSender) { if (pSender) { g_tab = static_cast(pSender->getTag()); @@ -394,6 +481,16 @@ void ModListLayer::onTab(CCObject* pSender) { 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() { // return global instance if one exists if (g_instance) return g_instance; diff --git a/loader/src/ui/internal/list/ModListLayer.hpp b/loader/src/ui/internal/list/ModListLayer.hpp index eadc9add..a9544819 100644 --- a/loader/src/ui/internal/list/ModListLayer.hpp +++ b/loader/src/ui/internal/list/ModListLayer.hpp @@ -1,13 +1,23 @@ #pragma once -#include "ModListView.hpp" - #include #include USE_GEODE_NAMESPACE(); class SearchFilterPopup; +class ModListCell; + +enum class ModListType { + Installed, + Download, + Featured, +}; + +enum class ModListDisplay { + Concise, + Expanded, +}; class ModListLayer : public CCLayer, public TextInputDelegate { protected: @@ -46,11 +56,18 @@ protected: void createSearchControl(); void onIndexUpdate(IndexUpdateEvent* event); + CCArray* createModCells(ModListType type); + CCSize getCellSize() const; + CCSize getListSize() const; + friend class SearchFilterPopup; public: static ModListLayer* create(); static ModListLayer* scene(); + void updateAllStates(ModListCell* except = nullptr); + + ModListDisplay getDisplay() const; void reloadList(); }; diff --git a/loader/src/ui/internal/list/ModListView.cpp b/loader/src/ui/internal/list/ModListView.cpp deleted file mode 100644 index 87a115c5..00000000 --- a/loader/src/ui/internal/list/ModListView.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "ModListView.hpp" - -#include "../info/CategoryNode.hpp" -#include "ModListLayer.hpp" -#include "ModListCell.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ModListView::updateAllStates(ModListCell* toggled) { - for (auto cell : CCArrayExt(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(obj)) { - as(cell)->loadFromMod(mod->mod); - } - if (auto mod = typeinfo_cast(obj)) { - // as(cell)->loadFromItem(mod->item); - } - if (auto failed = typeinfo_cast(obj)) { - as(cell)->loadFromInfo(failed->info); - } - as(cell)->updateBGColor(index); -} - -static void sortInstalledMods(std::vector& 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 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(); - } -} diff --git a/loader/src/ui/internal/list/ModListView.hpp b/loader/src/ui/internal/list/ModListView.hpp deleted file mode 100644 index b5f02019..00000000 --- a/loader/src/ui/internal/list/ModListView.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -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(); -}; diff --git a/loader/src/ui/internal/list/SearchFilterPopup.cpp b/loader/src/ui/internal/list/SearchFilterPopup.cpp index 504be2d5..718b6c6c 100644 --- a/loader/src/ui/internal/list/SearchFilterPopup.cpp +++ b/loader/src/ui/internal/list/SearchFilterPopup.cpp @@ -1,8 +1,7 @@ #include "SearchFilterPopup.hpp" -#include "../info/CategoryNode.hpp" +#include "../info/TagNode.hpp" #include "ModListLayer.hpp" -#include "ModListView.hpp" #include #include