diff --git a/loader/resources/tag-joke.png b/loader/resources/tag-joke.png new file mode 100644 index 00000000..4eee1136 Binary files /dev/null and b/loader/resources/tag-joke.png differ diff --git a/loader/resources/tag-modtober-long.png b/loader/resources/tag-modtober-long.png new file mode 100644 index 00000000..d53f2465 Binary files /dev/null and b/loader/resources/tag-modtober-long.png differ diff --git a/loader/resources/tag-modtober.png b/loader/resources/tag-modtober.png index 5b57a707..06359278 100644 Binary files a/loader/resources/tag-modtober.png and b/loader/resources/tag-modtober.png differ diff --git a/loader/resources/tag-paid-long.png b/loader/resources/tag-paid-long.png new file mode 100644 index 00000000..5e16735b Binary files /dev/null and b/loader/resources/tag-paid-long.png differ diff --git a/loader/resources/tag-paid.png b/loader/resources/tag-paid.png index 5e16735b..14a1471d 100644 Binary files a/loader/resources/tag-paid.png and b/loader/resources/tag-paid.png differ diff --git a/loader/src/ui/mods/ModsLayer.cpp b/loader/src/ui/mods/ModsLayer.cpp index df5c2204..4992f0e8 100644 --- a/loader/src/ui/mods/ModsLayer.cpp +++ b/loader/src/ui/mods/ModsLayer.cpp @@ -19,6 +19,8 @@ #include "ui/mods/sources/ModListSource.hpp" #include +static ModListDisplay MOD_LIST_DISPLAY = ModListDisplay::SmallList; + bool ModsStatusNode::init() { if (!CCNode::init()) return false; @@ -649,7 +651,7 @@ void ModsLayer::gotoTab(ModListSource* src) { m_currentSource = src; // Update the state of the current list - m_lists.at(m_currentSource)->updateDisplay(m_display); + m_lists.at(m_currentSource)->updateDisplay(MOD_LIST_DISPLAY); m_lists.at(m_currentSource)->activateSearch(m_showSearch); m_lists.at(m_currentSource)->updateState(); } @@ -711,7 +713,7 @@ void ModsLayer::updateState() { // Update display button for (auto btn : m_displayBtns) { static_cast(btn->getNormalImage())->setState( - static_cast(btn->getTag()) == m_display + static_cast(btn->getTag()) == MOD_LIST_DISPLAY ); } } @@ -744,10 +746,11 @@ void ModsLayer::onGoToPage(CCObject*) { popup->show(); } void ModsLayer::onDisplay(CCObject* sender) { - m_display = static_cast(sender->getTag()); + MOD_LIST_DISPLAY = static_cast(sender->getTag()); // Make sure to avoid a crash if (m_currentSource) { - m_lists.at(m_currentSource)->updateDisplay(m_display); + m_lists.at(m_currentSource)->updateDisplay(MOD_LIST_DISPLAY); + m_lists.at(m_currentSource)->reloadPage(); } this->updateState(); } diff --git a/loader/src/ui/mods/ModsLayer.hpp b/loader/src/ui/mods/ModsLayer.hpp index 35f40a14..43d080b4 100644 --- a/loader/src/ui/mods/ModsLayer.hpp +++ b/loader/src/ui/mods/ModsLayer.hpp @@ -66,7 +66,6 @@ protected: ModsStatusNode* m_statusNode; EventListener m_updateStateListener; bool m_showSearch = true; - ModListDisplay m_display = ModListDisplay::SmallList; std::vector m_displayBtns; bool init(); diff --git a/loader/src/ui/mods/list/ModItem.cpp b/loader/src/ui/mods/list/ModItem.cpp index 719c4660..fba92ceb 100644 --- a/loader/src/ui/mods/list/ModItem.cpp +++ b/loader/src/ui/mods/list/ModItem.cpp @@ -53,6 +53,10 @@ bool ModItem::init(ModSource&& source) { m_versionLabel->setID("version-label"); m_versionLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .7f)); m_titleContainer->addChild(m_versionLabel); + + m_versionDownloadSeparator = CCLabelBMFont::create("•", "bigFont.fnt"); + m_versionDownloadSeparator->setOpacity(155); + m_titleContainer->addChild(m_versionDownloadSeparator); m_titleContainer->setLayout( RowLayout::create() @@ -180,10 +184,7 @@ bool ModItem::init(ModSource&& source) { } } - auto viewBtn = CCMenuItemSpriteExtra::create( - spr, - this, menu_selector(ModItem::onView) - ); + auto viewBtn = CCMenuItemSpriteExtra::create(spr, this, menu_selector(ModItem::onView)); viewBtn->setID("view-button"); m_viewMenu->addChild(viewBtn); @@ -229,10 +230,23 @@ bool ModItem::init(ModSource&& source) { m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-featured.png"_spr)); } if (metadata.tags.contains("paid")) { - m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-paid.png"_spr)); + auto shortVer = CCSprite::createWithSpriteFrameName("tag-paid.png"_spr); + shortVer->setTag(1); + m_badgeContainer->addChild(shortVer); + auto longVer = CCSprite::createWithSpriteFrameName("tag-paid-long.png"_spr); + longVer->setTag(2); + m_badgeContainer->addChild(longVer); + } + if (metadata.tags.contains("joke")) { + m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-joke.png"_spr)); } if (metadata.tags.contains("modtober24")) { - m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr)); + auto shortVer = CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr); + shortVer->setTag(1); + m_badgeContainer->addChild(shortVer); + auto longVer = CCSprite::createWithSpriteFrameName("tag-modtober-long.png"_spr); + longVer->setTag(2); + m_badgeContainer->addChild(longVer); } // Show mod download count here already so people can make informed decisions @@ -242,17 +256,18 @@ bool ModItem::init(ModSource&& source) { auto downloads = CCLabelBMFont::create(numToAbbreviatedString(metadata.downloadCount).c_str(), "bigFont.fnt"); downloads->setID("downloads-label"); downloads->setColor("mod-list-version-label"_cc3b); - downloads->limitLabelWidth(80, .5f, .1f); + downloads->limitLabelWidth(125, 1.f, .1f); m_downloadCountContainer->addChildAtPosition(downloads, Anchor::Right, ccp(-0, 0), ccp(1, .5f)); auto downloadsIcon = CCSprite::createWithSpriteFrameName("GJ_downloadsIcon_001.png"); downloadsIcon->setID("downloads-icon-sprite"); - downloadsIcon->setScale(.75f); - m_downloadCountContainer->addChildAtPosition(downloadsIcon, Anchor::Left, ccp(5, 0)); + downloadsIcon->setScale(1.2f); + m_downloadCountContainer->addChildAtPosition(downloadsIcon, Anchor::Left, ccp(8, 0)); + // m_downloadCountContainer scale is controlled in updateState m_downloadCountContainer->setContentSize({ downloads->getScaledContentWidth() + downloadsIcon->getScaledContentWidth(), - 25 + 30 }); m_downloadCountContainer->updateLayout(); @@ -338,7 +353,7 @@ void ModItem::updateState() { // Update the size of the mod cell itself if (m_display == ModListDisplay::Grid) { - auto widthWithoutGaps = m_targetWidth - 10; + auto widthWithoutGaps = m_targetWidth - 7.5f; this->setContentSize(ccp(widthWithoutGaps / roundf(widthWithoutGaps / 80), 100)); m_bg->setContentSize(m_obContentSize / m_bg->getScale()); } @@ -358,14 +373,19 @@ void ModItem::updateState() { m_titleContainer->insertBefore(m_titleLabel, nullptr); } + // Show download separator if there is something to separate and we're in grid view + m_versionDownloadSeparator->setVisible(m_downloadCountContainer && m_display == ModListDisplay::Grid); + // Download counts go next to the version like on the website on grid view if (m_downloadCountContainer) { m_downloadCountContainer->removeFromParent(); if (m_display == ModListDisplay::Grid) { - m_titleContainer->insertAfter(m_downloadCountContainer, m_versionLabel); + m_titleContainer->insertAfter(m_downloadCountContainer, m_versionDownloadSeparator); + m_downloadCountContainer->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .7f)); } else { m_viewMenu->addChild(m_downloadCountContainer); + m_downloadCountContainer->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .6f)); } } @@ -373,14 +393,31 @@ void ModItem::updateState() { if (m_badgeContainer) { m_badgeContainer->removeFromParent(); if (m_display == ModListDisplay::Grid) { - m_badgeContainer->setLayout(ColumnLayout::create()->setAutoGrowAxis(true)); - m_badgeContainer->setScale(.25f); - this->addChildAtPosition(m_badgeContainer, Anchor::TopLeft, ccp(5, -5), ccp(0, 1)); + m_badgeContainer->setLayout( + ColumnLayout::create() + ->setAxisReverse(true) + ->setAutoGrowAxis(true) + ->setAxisAlignment(AxisAlignment::Start) + ); + m_badgeContainer->getLayout()->ignoreInvisibleChildren(true); + m_badgeContainer->setScale(.3f); + this->addChildAtPosition(m_badgeContainer, Anchor::TopLeft, ccp(5, -2), ccp(0, 1)); } else { - m_badgeContainer->setLayout(RowLayout::create()->setAutoGrowAxis(true)); + m_badgeContainer->setLayout( + RowLayout::create() + ->setAutoGrowAxis(true) + ); + m_badgeContainer->getLayout()->ignoreInvisibleChildren(true); m_titleContainer->addChild(m_badgeContainer); } + // Long tags don't fit in the grid UI + for (auto child : CCArrayExt(m_badgeContainer->getChildren())) { + if (child->getTag() > 0) { + child->setVisible(child->getTag() == (m_display == ModListDisplay::Grid ? 1 : 2)); + } + } + m_badgeContainer->updateLayout(); } // On Grid View logo has constant size @@ -558,6 +595,9 @@ void ModItem::updateState() { m_recommendedBy->updateLayout(); } + limitNodeWidth(m_downloadWaiting, m_titleContainer->getContentWidth(), 1.f, .1f); + limitNodeWidth(m_downloadBarContainer, m_titleContainer->getContentWidth(), 1.f, .1f); + // Update positioning (jesus) switch (m_display) { case ModListDisplay::Grid: { diff --git a/loader/src/ui/mods/list/ModItem.hpp b/loader/src/ui/mods/list/ModItem.hpp index 3891c3a1..be4bdeed 100644 --- a/loader/src/ui/mods/list/ModItem.hpp +++ b/loader/src/ui/mods/list/ModItem.hpp @@ -48,6 +48,7 @@ protected: Ref m_downloadCountContainer; ModListDisplay m_display = ModListDisplay::SmallList; float m_targetWidth = 300; + CCLabelBMFont* m_versionDownloadSeparator; /** * @warning Make sure `getMetadata` and `createModLogo` are callable diff --git a/loader/src/ui/mods/list/ModList.cpp b/loader/src/ui/mods/list/ModList.cpp index 530c32c3..123b1ac1 100644 --- a/loader/src/ui/mods/list/ModList.cpp +++ b/loader/src/ui/mods/list/ModList.cpp @@ -6,6 +6,22 @@ #include "../ModsLayer.hpp" #include "../popups/ModtoberPopup.hpp" +static size_t getDisplayPageSize(ModListSource* src, ModListDisplay display) { + if (src->isLocalModsOnly() && Mod::get()->template getSettingValue("infinite-local-mods-list")) { + return std::numeric_limits::max(); + } + return display == ModListDisplay::Grid ? 16 : 10; +} + +$execute { + listenForSettingChanges("infinite-local-mods-list", [](bool value) { + InstalledModListSource::get(InstalledModListType::All)->reset(); + InstalledModListSource::get(InstalledModListType::OnlyErrors)->reset(); + InstalledModListSource::get(InstalledModListType::OnlyOutdated)->reset(); + // Updates is technically a server mod list :-) So I left it out here + }); +} + bool ModList::init(ModListSource* src, CCSize const& size) { if (!CCNode::init()) return false; @@ -179,7 +195,7 @@ bool ModList::init(ModListSource* src, CCSize const& size) { m_searchInput->setCallback([this](auto const&) { // If the source is already in memory, we can immediately update the // search query - if (typeinfo_cast(m_source)) { + if (m_source->isLocalModsOnly()) { m_source->search(m_searchInput->getString()); return; } @@ -550,6 +566,7 @@ void ModList::updateTopContainer() { void ModList::updateDisplay(ModListDisplay display) { m_display = display; + m_source->setPageSize(getDisplayPageSize(m_source, m_display)); // Update all BaseModItems that are children of the list // There may be non-BaseModItems there (like separators) so gotta be type-safe @@ -570,7 +587,7 @@ void ModList::updateDisplay(ModListDisplay display) { m_list->m_contentLayer->setLayout( RowLayout::create() ->setGrowCrossAxis(true) - ->setAxisAlignment(AxisAlignment::Center) + ->setAxisAlignment(AxisAlignment::Start) ->setGap(2.5f) ); } @@ -634,6 +651,9 @@ void ModList::gotoPage(size_t page, bool update) { // Clear list contents m_list->m_contentLayer->removeAllChildren(); m_page = page; + + // Update page size (if needed) + m_source->setPageSize(getDisplayPageSize(m_source, m_display)); // Start loading new page with generic loading message this->showStatus(ModListUnkProgressStatus(), "Loading..."); diff --git a/loader/src/ui/mods/sources/InstalledModListSource.cpp b/loader/src/ui/mods/sources/InstalledModListSource.cpp index 91e6930e..fc996bb6 100644 --- a/loader/src/ui/mods/sources/InstalledModListSource.cpp +++ b/loader/src/ui/mods/sources/InstalledModListSource.cpp @@ -137,11 +137,8 @@ bool InstalledModListSource::isDefaultQuery() const { return m_query.isDefault(); } -$execute { - listenForSettingChanges("infinite-local-mods-list", [](bool value) { - auto size = value ? std::numeric_limits::max() : 10; - InstalledModListSource::get(InstalledModListType::All)->setPageSize(size); - InstalledModListSource::get(InstalledModListType::OnlyErrors)->setPageSize(size); - // Updates is technically a server mod list :-) So I left it out here - }); +bool InstalledModListSource::isLocalModsOnly() const { + return m_type == InstalledModListType::All || + m_type == InstalledModListType::OnlyErrors || + m_type == InstalledModListType::OnlyOutdated; } diff --git a/loader/src/ui/mods/sources/ModListSource.cpp b/loader/src/ui/mods/sources/ModListSource.cpp index 44900d20..62d49c99 100644 --- a/loader/src/ui/mods/sources/ModListSource.cpp +++ b/loader/src/ui/mods/sources/ModListSource.cpp @@ -62,8 +62,10 @@ std::optional ModListSource::getItemCount() const { return m_cachedItemCount; } void ModListSource::setPageSize(size_t size) { - m_pageSize = size; - this->reset(); + if (m_pageSize != size) { + m_pageSize = size; + this->reset(); + } } void ModListSource::reset() { diff --git a/loader/src/ui/mods/sources/ModListSource.hpp b/loader/src/ui/mods/sources/ModListSource.hpp index 7e241a9d..9e6e904d 100644 --- a/loader/src/ui/mods/sources/ModListSource.hpp +++ b/loader/src/ui/mods/sources/ModListSource.hpp @@ -77,6 +77,8 @@ public: std::optional getPageCount() const; std::optional getItemCount() const; void setPageSize(size_t size); + + virtual bool isLocalModsOnly() const = 0; static void clearAllCaches(); }; @@ -139,6 +141,8 @@ public: InstalledModsQuery const& getQuery() const; InvalidateQueryAfter getQueryMut(); bool isDefaultQuery() const override; + + bool isLocalModsOnly() const override; }; enum class ServerModListType { @@ -171,6 +175,8 @@ public: bool isDefaultQuery() const override; server::ModsQuery createDefaultQuery() const; ServerModListType getType() const; + + bool isLocalModsOnly() const override; }; class ModPackListSource : public ModListSource { @@ -187,6 +193,8 @@ public: std::unordered_set getModTags() const override; void setModTags(std::unordered_set const& tags) override; bool isDefaultQuery() const override; + + bool isLocalModsOnly() const override; }; bool weightedFuzzyMatch(std::string const& str, std::string const& kw, double weight, double& out); diff --git a/loader/src/ui/mods/sources/ModPackListSource.cpp b/loader/src/ui/mods/sources/ModPackListSource.cpp index d2d536ab..77d19fb2 100644 --- a/loader/src/ui/mods/sources/ModPackListSource.cpp +++ b/loader/src/ui/mods/sources/ModPackListSource.cpp @@ -21,3 +21,7 @@ void ModPackListSource::setModTags(std::unordered_set const& set) { bool ModPackListSource::isDefaultQuery() const { return true; } + +bool ModPackListSource::isLocalModsOnly() const { + return false; +} diff --git a/loader/src/ui/mods/sources/ServerModListSource.cpp b/loader/src/ui/mods/sources/ServerModListSource.cpp index 3dc54f83..f66b06ff 100644 --- a/loader/src/ui/mods/sources/ServerModListSource.cpp +++ b/loader/src/ui/mods/sources/ServerModListSource.cpp @@ -115,3 +115,7 @@ server::ModsQuery ServerModListSource::createDefaultQuery() const { ServerModListType ServerModListSource::getType() const { return m_type; } + +bool ServerModListSource::isLocalModsOnly() const { + return false; +}