mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-23 07:57:51 -05:00
add tags to ModPopup
This commit is contained in:
parent
c8a603cdbb
commit
1876af8124
7 changed files with 131 additions and 57 deletions
|
@ -1,5 +1,6 @@
|
|||
#include "GeodeStyle.hpp"
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/utils/ColorProvider.hpp>
|
||||
|
||||
bool GeodeSquareSprite::init(CCSprite* top, bool* state) {
|
||||
if (!CCSprite::initWithFile("GE_button_05.png"_spr))
|
||||
|
@ -74,6 +75,24 @@ CircleButtonSprite* createGeodeCircleButton(const char* topFrameName) {
|
|||
return CircleButtonSprite::createWithSpriteFrameName(topFrameName, 1.f, CircleBaseColor::DarkPurple);
|
||||
}
|
||||
|
||||
ButtonSprite* createGeodeTagLabel(std::string const& text, ccColor3B color, ccColor3B bg) {
|
||||
auto label = ButtonSprite::create(text.c_str(), "bigFont.fnt", "white-square.png"_spr, .8f);
|
||||
label->m_label->setColor(color);
|
||||
label->m_BGSprite->setColor(bg);
|
||||
return label;
|
||||
}
|
||||
|
||||
std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text) {
|
||||
static std::array TAG_COLORS {
|
||||
std::make_pair(ccc3(240, 233, 255), ccc3(130, 123, 163)),
|
||||
std::make_pair(ccc3(234, 255, 245), ccc3(123, 163, 136)),
|
||||
std::make_pair(ccc3(240, 252, 255), ccc3(123, 152, 163)),
|
||||
std::make_pair(ccc3(255, 253, 240), ccc3(163, 157, 123)),
|
||||
std::make_pair(ccc3(255, 242, 240), ccc3(163, 128, 123)),
|
||||
};
|
||||
return TAG_COLORS[hash(text) % 5932 % TAG_COLORS.size()];
|
||||
}
|
||||
|
||||
bool GeodeTabSprite::init(const char* iconFrame, const char* text, float width) {
|
||||
if (!CCNode::init())
|
||||
return false;
|
||||
|
|
|
@ -47,6 +47,9 @@ ButtonSprite* createGeodeButton(std::string const& text, std::string const& bg =
|
|||
|
||||
CircleButtonSprite* createGeodeCircleButton(const char* topFrameName);
|
||||
|
||||
ButtonSprite* createGeodeTagLabel(std::string const& text, ccColor3B color, ccColor3B bg);
|
||||
std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text);
|
||||
|
||||
class GeodeTabSprite : public CCNode {
|
||||
protected:
|
||||
CCScale9Sprite* m_deselectedBG;
|
||||
|
|
|
@ -72,17 +72,12 @@ bool ModItem::init(ModSource&& source) {
|
|||
);
|
||||
m_infoContainer->addChild(m_developers);
|
||||
|
||||
m_restartRequiredLabel = ButtonSprite::create("Restart Required", "bigFont.fnt", "white-square.png"_spr, .8f);
|
||||
m_restartRequiredLabel->m_label->setColor(
|
||||
ColorProvider::get()->define("mod-list-restart-required-label"_spr, ccc3(153, 245, 245))
|
||||
);
|
||||
m_restartRequiredLabel->m_BGSprite->setColor(
|
||||
m_restartRequiredLabel = createGeodeTagLabel(
|
||||
"Restart Required",
|
||||
ColorProvider::get()->define("mod-list-restart-required-label"_spr, ccc3(153, 245, 245)),
|
||||
ColorProvider::get()->define("mod-list-restart-required-label-bg"_spr, ccc3(123, 156, 163))
|
||||
);
|
||||
m_restartRequiredLabel->setLayoutOptions(
|
||||
AxisLayoutOptions::create()
|
||||
->setMaxScale(.75f)
|
||||
);
|
||||
m_restartRequiredLabel->setLayoutOptions(AxisLayoutOptions::create()->setMaxScale(.75f));
|
||||
m_infoContainer->addChild(m_restartRequiredLabel);
|
||||
|
||||
this->addChild(m_infoContainer);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <Geode/ui/MDTextArea.hpp>
|
||||
#include <Geode/utils/web.hpp>
|
||||
#include <Geode/ui/GeodeUI.hpp>
|
||||
#include <Geode/utils/ColorProvider.hpp>
|
||||
|
||||
bool ModPopup::setup(ModSource&& src) {
|
||||
m_source = std::move(src);
|
||||
|
@ -56,10 +57,6 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
idLabel->setOpacity(140);
|
||||
leftColumn->addChild(idLabel);
|
||||
|
||||
auto gap = CCNode::create();
|
||||
gap->setContentHeight(6);
|
||||
leftColumn->addChild(gap);
|
||||
|
||||
auto statsContainer = CCNode::create();
|
||||
statsContainer->setContentSize({ leftColumn->getContentWidth(), 80 });
|
||||
statsContainer->setAnchorPoint({ .5f, .5f });
|
||||
|
@ -146,6 +143,49 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
|
||||
leftColumn->addChild(statsContainer);
|
||||
|
||||
// Tags
|
||||
|
||||
auto tagsContainer = CCNode::create();
|
||||
tagsContainer->setContentSize({ leftColumn->getContentWidth(), 37.5f });
|
||||
tagsContainer->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
auto tagsBG = CCScale9Sprite::create("square02b_001.png");
|
||||
tagsBG->setColor({ 0, 0, 0 });
|
||||
tagsBG->setOpacity(75);
|
||||
tagsBG->setScale(.3f);
|
||||
tagsBG->setContentSize(tagsContainer->getContentSize() / tagsBG->getScale());
|
||||
tagsContainer->addChildAtPosition(tagsBG, Anchor::Center);
|
||||
|
||||
m_tags = CCNode::create();
|
||||
m_tags->ignoreAnchorPointForPosition(false);
|
||||
m_tags->setContentSize(tagsContainer->getContentSize() - ccp(10, 10));
|
||||
m_tags->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
// todo: refactor these spinners into a reusable class that's not the ass LoadingCircle is
|
||||
auto tagsSpinnerContainer = CCNode::create();
|
||||
tagsSpinnerContainer->setContentSize({ 50, 50 });
|
||||
tagsSpinnerContainer->setID("loading-spinner");
|
||||
|
||||
auto tagsSpinner = CCSprite::create("loadingCircle.png");
|
||||
tagsSpinner->setBlendFunc({ GL_ONE, GL_ONE });
|
||||
tagsSpinner->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f)));
|
||||
limitNodeSize(tagsSpinner, tagsSpinnerContainer->getContentSize(), 1.f, .1f);
|
||||
tagsSpinnerContainer->addChildAtPosition(tagsSpinner, Anchor::Center);
|
||||
|
||||
m_tags->addChild(tagsSpinnerContainer);
|
||||
|
||||
m_tags->setLayout(
|
||||
RowLayout::create()
|
||||
->setDefaultScaleLimits(.1f, .3f)
|
||||
->setGrowCrossAxis(true)
|
||||
->setCrossAxisOverflow(false)
|
||||
->setAxisAlignment(AxisAlignment::Start)
|
||||
->setGap(2)
|
||||
);
|
||||
tagsContainer->addChildAtPosition(m_tags, Anchor::Center);
|
||||
|
||||
leftColumn->addChild(tagsContainer);
|
||||
|
||||
// Installing
|
||||
|
||||
auto installContainer = CCNode::create();
|
||||
|
@ -190,50 +230,6 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
|
||||
leftColumn->addChild(installContainer);
|
||||
|
||||
// Options
|
||||
|
||||
auto optionsContainer = CCNode::create();
|
||||
optionsContainer->setContentSize({ leftColumn->getContentWidth(), 25 });
|
||||
optionsContainer->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
auto optionsBG = CCScale9Sprite::create("square02b_001.png");
|
||||
optionsBG->setColor({ 0, 0, 0 });
|
||||
optionsBG->setOpacity(75);
|
||||
optionsBG->setScale(.3f);
|
||||
optionsBG->setContentSize(optionsContainer->getContentSize() / optionsBG->getScale());
|
||||
optionsContainer->addChildAtPosition(optionsBG, Anchor::Center);
|
||||
|
||||
auto optionsMenu = CCMenu::create();
|
||||
optionsMenu->ignoreAnchorPointForPosition(false);
|
||||
optionsMenu->setContentSize(optionsContainer->getContentSize() - ccp(10, 10));
|
||||
optionsMenu->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
for (auto stat : std::initializer_list<std::tuple<
|
||||
const char*, const char*, SEL_MenuHandler
|
||||
>> {
|
||||
{ "folderIcon_001.png", "Config", nullptr },
|
||||
{ "folderIcon_001.png", "Save Data", nullptr },
|
||||
}) {
|
||||
auto spr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName(std::get<0>(stat)),
|
||||
std::get<1>(stat)
|
||||
);
|
||||
spr->setScale(.5f);
|
||||
auto btn = CCMenuItemSpriteExtra::create(
|
||||
spr, this, std::get<2>(stat)
|
||||
);
|
||||
optionsMenu->addChild(btn);
|
||||
}
|
||||
|
||||
optionsMenu->setLayout(
|
||||
RowLayout::create()
|
||||
->setDefaultScaleLimits(.1f, 1)
|
||||
->setAxisAlignment(AxisAlignment::Center)
|
||||
);
|
||||
optionsContainer->addChildAtPosition(optionsMenu, Anchor::Center);
|
||||
|
||||
leftColumn->addChild(optionsContainer);
|
||||
|
||||
// Links
|
||||
|
||||
auto linksContainer = CCNode::create();
|
||||
|
@ -361,6 +357,8 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
// Load stats from server (or just from the source if it already has them)
|
||||
m_statsListener.bind(this, &ModPopup::onLoadServerInfo);
|
||||
m_statsListener.setFilter(m_source.fetchServerInfo().listen());
|
||||
m_tagsListener.bind(this, &ModPopup::onLoadTags);
|
||||
m_tagsListener.setFilter(m_source.fetchValidTags().listen());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -457,6 +455,36 @@ void ModPopup::onLoadServerInfo(PromiseEvent<server::ServerModMetadata, server::
|
|||
}
|
||||
}
|
||||
|
||||
void ModPopup::onLoadTags(PromiseEvent<std::unordered_set<std::string>, server::ServerError>* event) {
|
||||
if (auto data = event->getResolve()) {
|
||||
m_tags->removeAllChildren();
|
||||
|
||||
for (auto& tag : *data) {
|
||||
auto readable = tag;
|
||||
readable[0] = std::toupper(readable[0]);
|
||||
auto colors = geodeTagColor(tag);
|
||||
m_tags->addChild(createGeodeTagLabel(readable, colors.first, colors.second));
|
||||
}
|
||||
|
||||
if (data->empty()) {
|
||||
auto label = CCLabelBMFont::create("No tags found", "bigFont.fnt");
|
||||
label->setOpacity(120);
|
||||
m_tags->addChild(label);
|
||||
}
|
||||
|
||||
m_tags->updateLayout();
|
||||
}
|
||||
else if (auto err = event->getReject()) {
|
||||
m_tags->removeAllChildren();
|
||||
|
||||
auto label = CCLabelBMFont::create("No tags found", "bigFont.fnt");
|
||||
label->setOpacity(120);
|
||||
m_tags->addChild(label);
|
||||
|
||||
m_tags->updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
void ModPopup::loadTab(ModPopup::Tab tab) {
|
||||
// Remove current page
|
||||
if (m_currentTabPage) {
|
||||
|
|
|
@ -17,10 +17,12 @@ protected:
|
|||
|
||||
ModSource m_source;
|
||||
CCNode* m_stats;
|
||||
CCNode* m_tags;
|
||||
CCNode* m_rightColumn;
|
||||
CCNode* m_currentTabPage = nullptr;
|
||||
std::unordered_map<Tab, std::pair<GeodeTabSprite*, Ref<CCNode>>> m_tabs;
|
||||
EventListener<PromiseEventFilter<server::ServerModMetadata, server::ServerError>> m_statsListener;
|
||||
EventListener<PromiseEventFilter<std::unordered_set<std::string>, server::ServerError>> m_tagsListener;
|
||||
|
||||
bool setup(ModSource&& src) override;
|
||||
|
||||
|
@ -29,6 +31,7 @@ protected:
|
|||
void setStatValue(CCNode* stat, std::optional<std::string> const& value);
|
||||
|
||||
void onLoadServerInfo(PromiseEvent<server::ServerModMetadata, server::ServerError>* event);
|
||||
void onLoadTags(PromiseEvent<std::unordered_set<std::string>, server::ServerError>* event);
|
||||
void loadTab(Tab tab);
|
||||
void onTab(CCObject* sender);
|
||||
|
||||
|
|
|
@ -57,3 +57,28 @@ server::ServerPromise<server::ServerModMetadata> ModSource::fetchServerInfo() co
|
|||
}
|
||||
}, m_value);
|
||||
}
|
||||
|
||||
server::ServerPromise<std::unordered_set<std::string>> ModSource::fetchValidTags() const {
|
||||
return std::visit(makeVisitor {
|
||||
[](Mod* mod) {
|
||||
return server::ServerResultCache<&server::getTags>::shared().get()
|
||||
.then<std::unordered_set<std::string>>([mod](std::unordered_set<std::string> validTags) {
|
||||
// Filter out invalid tags
|
||||
auto modTags = mod->getMetadata().getTags();
|
||||
auto finalTags = std::unordered_set<std::string>();
|
||||
for (auto& tag : modTags) {
|
||||
if (validTags.contains(tag)) {
|
||||
finalTags.insert(tag);
|
||||
}
|
||||
}
|
||||
return finalTags;
|
||||
});
|
||||
},
|
||||
[](server::ServerModMetadata const& metadata) {
|
||||
// Server info tags are always certain to be valid since the server has already validated them
|
||||
return server::ServerPromise<std::unordered_set<std::string>>([&metadata](auto resolve, auto) {
|
||||
resolve(metadata.tags);
|
||||
});
|
||||
}
|
||||
}, m_value);
|
||||
}
|
||||
|
|
|
@ -35,4 +35,5 @@ public:
|
|||
* for that
|
||||
*/
|
||||
server::ServerPromise<server::ServerModMetadata> fetchServerInfo() const;
|
||||
server::ServerPromise<std::unordered_set<std::string>> fetchValidTags() const;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue