better restarting ui

This commit is contained in:
HJfod 2024-02-29 21:37:09 +02:00
parent dac16a40cb
commit 72fa718a76
13 changed files with 164 additions and 51 deletions

View file

@ -64,6 +64,16 @@ namespace geode {
* `color` function)
*/
cocos2d::ccColor4B define(std::string const& id, cocos2d::ccColor4B const& color);
/**
* Define a new color with an associated ID. The ID should be prefixed
* with the mod ID. If the color has already been defined, nothing
* happens
* @param id The ID of the color; should be prefixed with mod ID
* @param color The color. Alpha component is assumed to be 255
* @returns The current value of the color with the ID (same as the
* `color` function, although with the value truncated to cc3b)
*/
cocos2d::ccColor3B define(std::string const& id, cocos2d::ccColor3B const& color);
/**
* Override the current value of a color with an associated ID
* @param id The ID of the color
@ -72,6 +82,15 @@ namespace geode {
* exist
*/
cocos2d::ccColor4B override(std::string const& id, cocos2d::ccColor4B const& color);
/**
* Override the current value of a color with an associated ID
* @param id The ID of the color
* @param color The color to override with. Alpha component is assumed
* to be 255
* @returns The new value of the color, or ccWHITE if the ID doesn't
* exist (truncated to cc3b)
*/
cocos2d::ccColor3B override(std::string const& id, cocos2d::ccColor3B const& color);
/**
* Reset the current value of a color to its original definition
* @param id The ID of the color

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,7 +1,7 @@
#include "GeodeStyle.hpp"
#include <Geode/utils/cocos.hpp>
bool GeodeButtonSprite::init(CCSprite* top, bool* state) {
bool GeodeSquareSprite::init(CCSprite* top, bool* state) {
if (!CCSprite::initWithFile("GE_button_05.png"_spr))
return false;
@ -18,7 +18,7 @@ bool GeodeButtonSprite::init(CCSprite* top, bool* state) {
return true;
}
void GeodeButtonSprite::update(float dt) {
void GeodeSquareSprite::update(float dt) {
CCSprite::update(dt);
if (m_stateSrc && m_state != *m_stateSrc) {
m_state = *m_stateSrc;
@ -28,8 +28,8 @@ void GeodeButtonSprite::update(float dt) {
}
}
GeodeButtonSprite* GeodeButtonSprite::create(const char* top, bool* state) {
auto ret = new GeodeButtonSprite();
GeodeSquareSprite* GeodeSquareSprite::create(const char* top, bool* state) {
auto ret = new GeodeSquareSprite();
if (ret && ret->init(CCSprite::create(top), state)) {
ret->autorelease();
return ret;
@ -38,8 +38,8 @@ GeodeButtonSprite* GeodeButtonSprite::create(const char* top, bool* state) {
return nullptr;
}
GeodeButtonSprite* GeodeButtonSprite::createWithSpriteFrameName(const char* top, bool* state) {
auto ret = new GeodeButtonSprite();
GeodeSquareSprite* GeodeSquareSprite::createWithSpriteFrameName(const char* top, bool* state) {
auto ret = new GeodeSquareSprite();
if (ret && ret->init(CCSprite::createWithSpriteFrameName(top), state)) {
ret->autorelease();
return ret;
@ -47,3 +47,7 @@ GeodeButtonSprite* GeodeButtonSprite::createWithSpriteFrameName(const char* top,
CC_SAFE_DELETE(ret);
return nullptr;
}
ButtonSprite* createGeodeButton(std::string const& text) {
return ButtonSprite::create(text.c_str(), "bigFont.fnt", "GE_button_05.png"_spr, .8f);
}

View file

@ -4,7 +4,7 @@
using namespace geode::prelude;
class GeodeButtonSprite : public CCSprite {
class GeodeSquareSprite : public CCSprite {
protected:
bool* m_stateSrc = nullptr;
bool m_state = false;
@ -14,6 +14,16 @@ protected:
void update(float dt) override;
public:
static GeodeButtonSprite* create(const char* top, bool* state = nullptr);
static GeodeButtonSprite* createWithSpriteFrameName(const char* top, bool* state = nullptr);
static GeodeSquareSprite* create(const char* top, bool* state = nullptr);
static GeodeSquareSprite* createWithSpriteFrameName(const char* top, bool* state = nullptr);
};
ButtonSprite* createGeodeButton(std::string const& text);
class GeodeButtonSprite : public ButtonSprite {
protected:
bool init(std::string const& text);
public:
static GeodeButtonSprite* create(std::string const& text);
};

View file

@ -1,5 +1,7 @@
#include "ModItem.hpp"
#include <Geode/ui/GeodeUI.hpp>
#include <Geode/utils/ColorProvider.hpp>
#include "GeodeStyle.hpp"
bool BaseModItem::init() {
if (!CCNode::init())
@ -56,8 +58,11 @@ bool BaseModItem::init() {
);
m_infoContainer->addChild(m_developers);
m_restartRequiredLabel = ButtonSprite::create("Restart Required", "bigFont.fnt", "black-square.png"_spr, .8f);
m_restartRequiredLabel->m_label->setColor({ 55, 255, 155 });
m_restartRequiredLabel = ButtonSprite::create("Restart Required", "bigFont.fnt", "white-square.png"_spr, .8f);
m_restartRequiredLabel->m_label->setColor({ 153, 245, 245 });
m_restartRequiredLabel->m_BGSprite->setColor(
ColorProvider::get()->define("mod-list-label-bg"_spr, ccc3(123, 156, 163))
);
m_restartRequiredLabel->setLayoutOptions(
AxisLayoutOptions::create()
->setMaxScale(.75f)
@ -71,8 +76,7 @@ bool BaseModItem::init() {
m_viewMenu->setScale(.55f);
auto viewBtn = CCMenuItemSpriteExtra::create(
ButtonSprite::create("View", "bigFont.fnt", "GE_button_05.png"_spr, .8f),
this, nullptr
createGeodeButton("View"), this, nullptr
);
m_viewMenu->addChild(viewBtn);
@ -92,6 +96,11 @@ void BaseModItem::updateState() {
m_restartRequiredLabel->setVisible(wantsRestart);
m_developers->setVisible(!wantsRestart);
m_infoContainer->updateLayout();
// Propagate update up the chain
if (m_updateParentState) {
m_updateParentState();
}
}
void BaseModItem::updateSize(float width, bool big) {
@ -124,6 +133,10 @@ void BaseModItem::updateSize(float width, bool big) {
this->updateLayout();
}
void BaseModItem::onUpdateParentState(MiniFunction<void()> listener) {
m_updateParentState = listener;
}
bool InstalledModItem::init(Mod* mod) {
m_mod = mod;

View file

@ -13,6 +13,7 @@ protected:
CCNode* m_developers;
ButtonSprite* m_restartRequiredLabel = nullptr;
CCMenu* m_viewMenu;
MiniFunction<void()> m_updateParentState = nullptr;
/**
* @warning Make sure `getMetadata` and `createModLogo` are callable
@ -20,13 +21,18 @@ protected:
*/
bool init();
// This should never be exposed outside, so the parent can't call this and
// cause an infinite loop during state updating
virtual void updateState();
public:
virtual ModMetadata getMetadata() const = 0;
virtual CCNode* createModLogo() const = 0;
virtual bool wantsRestart() const = 0;
virtual void updateSize(float width, bool big);
virtual void updateState();
void onUpdateParentState(MiniFunction<void()> listener);
};
class InstalledModItem : public BaseModItem {
@ -38,6 +44,8 @@ protected:
void onEnable(CCObject*);
void updateState() override;
public:
/**
* @note Make sure to call `updateSize` afterwards
@ -47,8 +55,6 @@ public:
ModMetadata getMetadata() const override;
CCNode* createModLogo() const override;
bool wantsRestart() const override;
void updateState() override;
};
class ServerModItem : public BaseModItem {

View file

@ -72,13 +72,13 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
// todo: sort button
auto filterBtn = CCMenuItemSpriteExtra::create(
GeodeButtonSprite::createWithSpriteFrameName("GJ_filterIcon_001.png"),
GeodeSquareSprite::createWithSpriteFrameName("GJ_filterIcon_001.png"),
this, menu_selector(ModList::onFilters)
);
searchFiltersMenu->addChild(filterBtn);
auto clearFiltersBtn = CCMenuItemSpriteExtra::create(
GeodeButtonSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png"),
GeodeSquareSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png"),
this, menu_selector(ModList::onClearFilters)
);
searchFiltersMenu->addChild(clearFiltersBtn);
@ -196,6 +196,7 @@ void ModList::onPromise(typename ModListSource::PageLoadEvent* event) {
}
first = false;
m_list->m_contentLayer->addChild(item);
item->onUpdateParentState(m_updateParentState);
}
this->updateSize(m_bigSize);
@ -320,8 +321,8 @@ void ModList::updatePageNumber() {
m_pageNextBtn->setVisible(pageCount && m_page < pageCount.value() - 1);
// Notify container about page count update
if (m_pageUpdated) {
m_pageUpdated();
if (m_updateParentState) {
m_updateParentState();
}
}
@ -373,8 +374,8 @@ void ModList::showStatus(ModListStatus status, std::string const& message, std::
m_statusContainer->updateLayout();
}
void ModList::onPageUpdated(ModListPageUpdated listener) {
m_pageUpdated = listener;
void ModList::onUpdateParentState(MiniFunction<void()> listener) {
m_updateParentState = listener;
}
void ModList::onFilters(CCObject*) {

View file

@ -16,8 +16,6 @@ struct ModListProgressStatus {
};
using ModListStatus = std::variant<ModListErrorStatus, ModListUnkProgressStatus, ModListProgressStatus>;
using ModListPageUpdated = MiniFunction<void()>;
class ModList : public CCNode {
protected:
Ref<ModListSource> m_source;
@ -34,7 +32,7 @@ protected:
CCMenuItemSpriteExtra* m_pageNextBtn;
Ref<CCNode> m_searchMenu;
TextInput* m_searchInput;
ModListPageUpdated m_pageUpdated = nullptr;
MiniFunction<void()> m_updateParentState = nullptr;
bool m_bigSize = false;
std::atomic<size_t> m_searchInputThreads = 0;
@ -50,7 +48,7 @@ public:
static ModList* create(ModListSource* src, CCSize const& size);
// poor man's delegate
void onPageUpdated(ModListPageUpdated listener);
void onUpdateParentState(MiniFunction<void()> listener);
size_t getPage() const;
void reloadPage();

View file

@ -109,7 +109,7 @@ static auto loadServerModsPage(server::ModsQuery&& query) {
typename ModListSource::PagePromise ModListSource::loadPage(size_t page, bool update) {
// Return a generic "Coming soon" message if there's no provider set
if (!m_provider) {
if (!m_provider.get) {
return PagePromise([this, page](auto, auto reject) {
reject("Coming soon! ;)");
});
@ -123,7 +123,7 @@ typename ModListSource::PagePromise ModListSource::loadPage(size_t page, bool up
}
m_cachedPages.erase(page);
return PagePromise([this, page](auto resolve, auto reject, auto progress, auto cancelled) {
m_provider(server::ModsQuery {
m_provider.get(server::ModsQuery {
.query = m_query,
.page = page,
// todo: loader setting to change this
@ -171,13 +171,16 @@ void ModListSource::setQuery(std::string const& query) {
}
bool ModListSource::isInMemory() const {
return m_inMemory;
return m_provider.inMemory;
}
ModListSource* ModListSource::create(Provider* provider, bool inMemory) {
bool ModListSource::wantsRestart() const {
return m_provider.wantsRestart && m_provider.wantsRestart();
}
ModListSource* ModListSource::create(Provider&& provider) {
auto ret = new ModListSource();
ret->m_provider = provider;
ret->m_inMemory = inMemory;
ret->m_provider = std::move(provider);
ret->autorelease();
return ret;
}
@ -186,32 +189,45 @@ ModListSource* ModListSource::get(ModListSourceType type) {
switch (type) {
default:
case ModListSourceType::Installed: {
static auto inst = Ref(ModListSource::create(loadInstalledModsPage, true));
static auto inst = Ref(ModListSource::create({
.get = loadInstalledModsPage,
.wantsRestart = +[] {
for (auto mod : Loader::get()->getAllMods()) {
if (mod->getRequestedAction() != ModRequestedAction::None) {
return true;
}
}
return false;
},
.inMemory = true,
}));
return inst;
} break;
case ModListSourceType::Featured: {
static auto inst = Ref(ModListSource::create(+[](server::ModsQuery&& query) {
query.featured = true;
return loadServerModsPage(std::move(query));
}, false));
static auto inst = Ref(ModListSource::create({
.get = +[](server::ModsQuery&& query) {
query.featured = true;
return loadServerModsPage(std::move(query));
},
}));
return inst;
} break;
case ModListSourceType::Trending: {
static auto inst = Ref(ModListSource::create(nullptr, false));
static auto inst = Ref(ModListSource::create({}));
return inst;
} break;
case ModListSourceType::ModPacks: {
static auto inst = Ref(ModListSource::create(nullptr, false));
static auto inst = Ref(ModListSource::create({}));
return inst;
} break;
case ModListSourceType::All: {
static auto inst = Ref(ModListSource::create(+[](server::ModsQuery&& query) {
return loadServerModsPage(std::move(query));
}, false));
static auto inst = Ref(ModListSource::create({
.get = loadServerModsPage,
}));
return inst;
} break;
}

View file

@ -34,18 +34,22 @@ public:
using PagePromise = Promise<Page, LoadPageError, std::optional<uint8_t>>;
using ProviderPromise = Promise<std::pair<Page, size_t>, LoadPageError, std::optional<uint8_t>>;
using Provider = ProviderPromise(server::ModsQuery&& query);
struct Provider {
ProviderPromise(*get)(server::ModsQuery&& query) = nullptr;
bool(*wantsRestart)() = nullptr;
bool inMemory = false;
};
protected:
std::unordered_map<size_t, Page> m_cachedPages;
std::optional<size_t> m_cachedItemCount;
std::optional<std::string> m_query;
Provider* m_provider = nullptr;
bool m_inMemory;
Provider m_provider;
public:
// Create a new source with an arbitary provider
static ModListSource* create(Provider* provider, bool inMemory);
static ModListSource* create(Provider&& provider);
// Get a standard source (lazily created static instance)
static ModListSource* get(ModListSourceType type);
@ -69,4 +73,5 @@ public:
* instantaniously or buffer a bit to avoid spamming unnecessary requests
*/
bool isInMemory() const;
bool wantsRestart() const;
};

View file

@ -139,13 +139,13 @@ bool ModsLayer::init() {
listActionsMenu->setScale(.65f);
auto bigSizeBtn = CCMenuItemSpriteExtra::create(
GeodeButtonSprite::createWithSpriteFrameName("GJ_smallModeIcon_001.png", &m_bigView),
GeodeSquareSprite::createWithSpriteFrameName("GJ_smallModeIcon_001.png", &m_bigView),
this, menu_selector(ModsLayer::onBigView)
);
listActionsMenu->addChild(bigSizeBtn);
auto searchBtn = CCMenuItemSpriteExtra::create(
GeodeButtonSprite::createWithSpriteFrameName("search.png"_spr, &m_showSearch),
GeodeSquareSprite::createWithSpriteFrameName("search.png"_spr, &m_showSearch),
this, menu_selector(ModsLayer::onSearch)
);
listActionsMenu->addChild(searchBtn);
@ -153,6 +153,20 @@ bool ModsLayer::init() {
listActionsMenu->setLayout(ColumnLayout::create());
m_frame->addChildAtPosition(listActionsMenu, Anchor::Left, ccp(-5, 25));
auto restartMenu = CCMenu::create();
restartMenu->setContentWidth(200.f);
restartMenu->setAnchorPoint({ .5f, 1.f });
restartMenu->setScale(.7f);
m_restartBtn = CCMenuItemSpriteExtra::create(
createGeodeButton("Restart Now"),
this, menu_selector(ModsLayer::onRestart)
);
restartMenu->addChild(m_restartBtn);
restartMenu->setLayout(RowLayout::create());
m_frame->addChildAtPosition(restartMenu, Anchor::Bottom, ccp(0, 0));
m_pageMenu = CCMenu::create();
m_pageMenu->setContentWidth(200.f);
m_pageMenu->setAnchorPoint({ 1.f, 1.f });
@ -180,6 +194,8 @@ bool ModsLayer::init() {
this->setKeypadEnabled(true);
cocos::handleTouchPriority(this, true);
this->updateState();
return true;
}
@ -204,7 +220,7 @@ void ModsLayer::gotoTab(ModListSourceType type) {
// Lazily create new list and add it to UI
if (!m_lists.contains(src)) {
auto list = ModList::create(src, m_frame->getContentSize() - ccp(30, 0));
list->onPageUpdated(std::bind(&ModsLayer::updatePageNumber, this));
list->onUpdateParentState(std::bind(&ModsLayer::updateState, this));
list->setPosition(m_frame->getPosition());
this->addChild(list);
m_lists.emplace(src, list);
@ -237,7 +253,7 @@ void ModsLayer::setTextPopupClosed(SetTextPopup* popup, gd::string value) {
}
}
void ModsLayer::updatePageNumber() {
void ModsLayer::updateState() {
// Show current page number if the current source has total page count loaded
if (m_currentSource && m_currentSource->getPageCount()) {
auto page = m_lists.at(m_currentSource)->getPage() + 1;
@ -256,6 +272,14 @@ void ModsLayer::updatePageNumber() {
else {
m_pageMenu->setVisible(false);
}
// Update visibility of the restart button
m_restartBtn->setVisible(false);
for (auto& [src, _] : m_lists) {
if (src->wantsRestart()) {
m_restartBtn->setVisible(true);
}
}
}
void ModsLayer::onTab(CCObject* sender) {
@ -294,6 +318,15 @@ void ModsLayer::onSearch(CCObject*) {
}
}
void ModsLayer::onRestart(CCObject*) {
// Update button state to let user know it's restarting but it might take a bit
m_restartBtn->setEnabled(false);
static_cast<ButtonSprite*>(m_restartBtn->getNormalImage())->setString("Restarting...");
// Actually restart
game::restart();
}
ModsLayer* ModsLayer::create() {
auto ret = new ModsLayer();
if (ret && ret->init()) {

View file

@ -18,6 +18,7 @@ protected:
CCMenu* m_pageMenu;
CCLabelBMFont* m_pageLabel;
CCMenuItemSpriteExtra* m_goToPageBtn;
CCMenuItemSpriteExtra* m_restartBtn;
bool m_showSearch = false;
bool m_bigView = false;
@ -32,8 +33,9 @@ protected:
void onGoToPage(CCObject*);
void onBack(CCObject*);
void onRefreshList(CCObject*);
void onRestart(CCObject*);
void updatePageNumber();
void updateState();
public:
static ModsLayer* create();

View file

@ -31,6 +31,9 @@ ccColor4B ColorProvider::define(std::string const& id, ccColor4B const& color) {
m_impl->colors.insert({ id, std::pair(color, std::nullopt) });
return this->color(id);
}
ccColor3B ColorProvider::define(std::string const& id, ccColor3B const& color) {
return to3B(this->define(id, to4B(color)));
}
ccColor4B ColorProvider::override(std::string const& id, ccColor4B const& color) {
if (m_impl->colors.contains(id)) {
m_impl->colors.at(id).second = color;
@ -42,6 +45,9 @@ ccColor4B ColorProvider::override(std::string const& id, ccColor4B const& color)
return to4B(ccWHITE);
}
}
ccColor3B ColorProvider::override(std::string const& id, ccColor3B const& color) {
return to3B(this->override(id, to4B(color)));
}
ccColor4B ColorProvider::reset(std::string const& id) {
if (m_impl->colors.contains(id)) {
m_impl->colors.at(id).second = std::nullopt;