mirror of
https://github.com/geode-sdk/geode.git
synced 2025-02-17 00:30:26 -05:00
new mods list design is done :3 ready to impl new index!!
This commit is contained in:
parent
e82ea3eddd
commit
2db556dc20
4 changed files with 355 additions and 141 deletions
|
@ -2,43 +2,59 @@
|
|||
|
||||
static size_t INSTALLED_MODS_PAGE_SIZE = 10;
|
||||
|
||||
struct InstalledModsList : ModListSource {
|
||||
std::string_view getID() const override {
|
||||
return "installed";
|
||||
Promise<typename ModListSource::Page> ModListSource::loadPage(size_t page, bool update) {
|
||||
if (!update && m_cachedPages.contains(page)) {
|
||||
return Promise<Page>([this, page](auto resolve, auto) { resolve(m_cachedPages.at(page)); });
|
||||
}
|
||||
|
||||
Promise<Page> loadNewPage(size_t page, bool) {
|
||||
// You can't load new mods at runtime so update does nothing
|
||||
return Promise([](auto resolve, auto _reject, auto _progress) {
|
||||
std::thread([res = std::move(resolve)] {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
auto page = Page();
|
||||
auto all = Loader::get()->getAllMods();
|
||||
for (size_t i = page * INSTALLED_MODS_PAGE_SIZE; i < all.size(); i += 1) {
|
||||
page.push_back(InstalledModItem::create(all.at(i)));
|
||||
}
|
||||
res(page);
|
||||
}).detach();
|
||||
});
|
||||
}
|
||||
|
||||
Promise<size_t> loadTotalPageCount() const {
|
||||
return Promise([](auto resolve, auto _reject, auto _progress) {
|
||||
resolve(Loader::get()->getAllMods().size() / INSTALLED_MODS_PAGE_SIZE + 1);
|
||||
});
|
||||
}
|
||||
|
||||
static InstalledModsList& get() {
|
||||
static auto inst = InstalledModsList();
|
||||
return inst;
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<std::reference_wrapper<ModListSource>> ModListSource::get(std::string_view id) {
|
||||
switch (hash(id)) {
|
||||
case hash("installed"): {
|
||||
return InstalledModsList::get();
|
||||
} break;
|
||||
}
|
||||
return std::nullopt;
|
||||
m_cachedPages.erase(page);
|
||||
return Promise<Page>([this, page](auto resolve, auto reject, auto progress, auto const&) {
|
||||
this->reloadPage(page)
|
||||
.then([page, this, resolve = std::move(resolve)](auto data) {
|
||||
m_cachedPages.insert({ page, data });
|
||||
resolve(data);
|
||||
})
|
||||
.expect([this, reject = std::move(reject)](auto error) {
|
||||
reject(error);
|
||||
})
|
||||
.progress([this, progress = std::move(progress)](auto prog) {
|
||||
progress(prog);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<size_t> ModListSource::getPageCount() const {
|
||||
return m_cachedPageCount;
|
||||
}
|
||||
|
||||
Promise<typename ModListSource::Page> InstalledModsList::reloadPage(size_t page) {
|
||||
m_cachedPageCount = Loader::get()->getAllMods().size() / INSTALLED_MODS_PAGE_SIZE + 1;
|
||||
return Promise<Page>([page](auto resolve, auto, auto, auto const&) {
|
||||
auto content = Page();
|
||||
auto all = Loader::get()->getAllMods();
|
||||
for (
|
||||
size_t i = page * INSTALLED_MODS_PAGE_SIZE;
|
||||
i < all.size() && i < (page + 1) * INSTALLED_MODS_PAGE_SIZE;
|
||||
i += 1
|
||||
) {
|
||||
content.push_back(InstalledModItem::create(all.at(i)));
|
||||
}
|
||||
resolve(content);
|
||||
});
|
||||
}
|
||||
|
||||
InstalledModsList* InstalledModsList::get() {
|
||||
static auto inst = new InstalledModsList();
|
||||
return inst;
|
||||
}
|
||||
|
||||
Promise<typename ModListSource::Page> ModPacksModsList::reloadPage(size_t page) {
|
||||
m_cachedPageCount = 0;
|
||||
return Promise<Page>([](auto, auto reject) {
|
||||
reject("Coming soon! ;)");
|
||||
});
|
||||
}
|
||||
|
||||
ModPacksModsList* ModPacksModsList::get() {
|
||||
static auto inst = new ModPacksModsList();
|
||||
return inst;
|
||||
}
|
||||
|
|
|
@ -7,18 +7,36 @@
|
|||
using namespace geode::prelude;
|
||||
|
||||
// Handles loading the entries for the mods list
|
||||
struct ModListSource {
|
||||
class ModListSource {
|
||||
public:
|
||||
using Page = std::vector<Ref<BaseModItem>>;
|
||||
|
||||
// ID of the list source
|
||||
virtual std::string_view getID() const = 0;
|
||||
using PageLoadEventListener = EventListener<PromiseEventFilter<Page>>;
|
||||
|
||||
// Load a page. Use update to force a reload on existing page. Up to the
|
||||
// source impl to cache this
|
||||
virtual Promise<Page> loadNewPage(size_t page, bool update = false) = 0;
|
||||
protected:
|
||||
std::unordered_map<size_t, Page> m_cachedPages;
|
||||
std::optional<size_t> m_cachedPageCount;
|
||||
|
||||
// Get the total number of available pages
|
||||
virtual Promise<size_t> loadTotalPageCount() const = 0;
|
||||
// Load/reload a page. This should also set/update the page count
|
||||
virtual Promise<Page> reloadPage(size_t page) = 0;
|
||||
|
||||
static std::optional<std::reference_wrapper<ModListSource>> get(std::string_view id);
|
||||
public:
|
||||
// Load page, uses cache if possible unless `update` is true
|
||||
Promise<Page> loadPage(size_t page, bool update = false);
|
||||
std::optional<size_t> getPageCount() const;
|
||||
};
|
||||
|
||||
class InstalledModsList : public ModListSource {
|
||||
protected:
|
||||
Promise<Page> reloadPage(size_t page) override;
|
||||
|
||||
public:
|
||||
static InstalledModsList* get();
|
||||
};
|
||||
|
||||
class ModPacksModsList : public ModListSource {
|
||||
protected:
|
||||
Promise<Page> reloadPage(size_t page) override;
|
||||
|
||||
public:
|
||||
static ModPacksModsList* get();
|
||||
};
|
||||
|
|
|
@ -3,6 +3,201 @@
|
|||
|
||||
static bool BIG_VIEW = false;
|
||||
|
||||
bool ModList::init(ModListSource* src, CCSize const& size) {
|
||||
if (!CCNode::init())
|
||||
return false;
|
||||
|
||||
this->setContentSize(size);
|
||||
this->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
m_source = src;
|
||||
|
||||
m_list = ScrollLayer::create(size);
|
||||
m_list->m_contentLayer->setLayout(
|
||||
ColumnLayout::create()
|
||||
->setAxisReverse(true)
|
||||
->setAxisAlignment(AxisAlignment::End)
|
||||
->setAutoGrowAxis(size.height)
|
||||
);
|
||||
this->addChildAtPosition(m_list, Anchor::Center, -m_list->getScaledContentSize() / 2);
|
||||
|
||||
auto pageLeftMenu = CCMenu::create();
|
||||
pageLeftMenu->setContentWidth(30.f);
|
||||
pageLeftMenu->setAnchorPoint({ 1.f, .5f });
|
||||
|
||||
m_pagePrevBtn = CCMenuItemSpriteExtra::create(
|
||||
CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png"),
|
||||
this, menu_selector(ModList::onPage)
|
||||
);
|
||||
m_pagePrevBtn->setTag(-1);
|
||||
pageLeftMenu->addChild(m_pagePrevBtn);
|
||||
|
||||
pageLeftMenu->setLayout(
|
||||
RowLayout::create()
|
||||
->setAxisAlignment(AxisAlignment::End)
|
||||
->setAxisReverse(true)
|
||||
);
|
||||
this->addChildAtPosition(pageLeftMenu, Anchor::Left, ccp(-5, 0));
|
||||
|
||||
auto pageRightMenu = CCMenu::create();
|
||||
pageRightMenu->setContentWidth(30.f);
|
||||
pageRightMenu->setAnchorPoint({ 0.f, .5f });
|
||||
|
||||
auto pageNextSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png");
|
||||
pageNextSpr->setFlipX(true);
|
||||
m_pageNextBtn = CCMenuItemSpriteExtra::create(
|
||||
pageNextSpr,
|
||||
this, menu_selector(ModList::onPage)
|
||||
);
|
||||
m_pageNextBtn->setTag(1);
|
||||
pageRightMenu->addChild(m_pageNextBtn);
|
||||
|
||||
pageRightMenu->setLayout(
|
||||
RowLayout::create()
|
||||
->setAxisAlignment(AxisAlignment::Start)
|
||||
);
|
||||
this->addChildAtPosition(pageRightMenu, Anchor::Right, ccp(5, 0));
|
||||
|
||||
m_pageLabel = CCLabelBMFont::create("", "bigFont.fnt");
|
||||
m_pageLabel->setAnchorPoint({ .5f, 1.f });
|
||||
this->addChildAtPosition(m_pageLabel, Anchor::Bottom, ccp(0, -5));
|
||||
|
||||
m_statusText = SimpleTextArea::create("", "bigFont.fnt", .6f);
|
||||
m_statusText->setAlignment(kCCTextAlignmentCenter);
|
||||
this->addChildAtPosition(m_statusText, Anchor::Center, ccp(0, 40));
|
||||
|
||||
m_statusLoadingCircle = CCSprite::create("loadingCircle.png");
|
||||
m_statusLoadingCircle->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f)));
|
||||
m_statusLoadingCircle->setScale(.6f);
|
||||
this->addChildAtPosition(m_statusLoadingCircle, Anchor::Center, ccp(0, -40));
|
||||
|
||||
m_listener.bind(this, &ModList::onPromise);
|
||||
|
||||
this->gotoPage(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModList::onPromise(PromiseEvent<typename ModListSource::Page>* event) {
|
||||
if (auto resolved = event->getResolve()) {
|
||||
// Hide status
|
||||
m_statusText->setVisible(false);
|
||||
m_statusLoadingCircle->setVisible(false);
|
||||
|
||||
// Create items
|
||||
for (auto item : *resolved) {
|
||||
m_list->m_contentLayer->addChild(item);
|
||||
item->updateSize(m_list->getContentWidth(), BIG_VIEW);
|
||||
}
|
||||
// Auto-grow the size of the list content
|
||||
m_list->m_contentLayer->updateLayout();
|
||||
|
||||
// Scroll list to top
|
||||
auto listTopScrollPos = -m_list->m_contentLayer->getContentHeight() + m_list->getContentHeight();
|
||||
m_list->m_contentLayer->setPositionY(listTopScrollPos);
|
||||
|
||||
// Update page UI
|
||||
this->updatePageUI();
|
||||
}
|
||||
else if (auto progress = event->getProgress()) {
|
||||
// todo: percentage in a loading bar
|
||||
this->showStatus(progress->message, true);
|
||||
}
|
||||
else if (auto rejected = event->getReject()) {
|
||||
this->showStatus(*rejected, false);
|
||||
this->updatePageUI(true);
|
||||
}
|
||||
|
||||
if (event->isFinally()) {
|
||||
// Clear listener
|
||||
m_listener.setFilter(PromiseEventFilter<ModListSource::Page>());
|
||||
}
|
||||
}
|
||||
|
||||
void ModList::onPage(CCObject* sender) {
|
||||
// If no page count has been loaded yet, we can't do anything
|
||||
if (!m_source->getPageCount()) return;
|
||||
auto pageCount = m_source->getPageCount().value();
|
||||
|
||||
// Make sure you can't go beyond the limits
|
||||
if (sender->getTag() < 0 && m_page >= -sender->getTag()) {
|
||||
m_page += sender->getTag();
|
||||
}
|
||||
// Ig this can technically overflow, but why would there be over 4 billion pages
|
||||
// (and why would someone manually scroll that far)
|
||||
else if (sender->getTag() > 0 && m_page + sender->getTag() < m_source->getPageCount()) {
|
||||
m_page += sender->getTag();
|
||||
}
|
||||
|
||||
// Load new page
|
||||
this->gotoPage(m_page);
|
||||
}
|
||||
|
||||
void ModList::updatePageUI(bool hide) {
|
||||
auto pageCount = m_source->getPageCount();
|
||||
|
||||
// Clamp page count in case the max amount has changed for some reason
|
||||
if (pageCount && m_page >= pageCount.value()) {
|
||||
auto count = pageCount.value();
|
||||
m_page = count > 0 ? count - 1 : 0;
|
||||
}
|
||||
|
||||
// Hide if page count hasn't been loaded
|
||||
if (!pageCount) {
|
||||
hide = true;
|
||||
}
|
||||
m_pagePrevBtn->setVisible(!hide && m_page > 0);
|
||||
m_pageNextBtn->setVisible(!hide && m_page < pageCount.value() - 1);
|
||||
m_pageLabel->setVisible(!hide);
|
||||
if (pageCount > 0u) {
|
||||
auto fmt = fmt::format("Page {}/{}", m_page + 1, pageCount.value());
|
||||
m_pageLabel->setString(fmt.c_str());
|
||||
m_pageLabel->limitLabelWidth(100.f, .35f, .1f);
|
||||
}
|
||||
}
|
||||
|
||||
void ModList::reloadPage() {
|
||||
// Just force an update on the current page
|
||||
this->gotoPage(m_page, true);
|
||||
}
|
||||
|
||||
void ModList::gotoPage(size_t page, bool update) {
|
||||
// Clear list contents
|
||||
m_list->m_contentLayer->removeAllChildren();
|
||||
m_page = page;
|
||||
|
||||
// Start loading new page with generic loading message
|
||||
this->showStatus("Loading...", true);
|
||||
m_listener.setFilter(m_source->loadPage(page, update).listen());
|
||||
|
||||
// Do initial eager update on page UI (to prevent user spamming arrows
|
||||
// to access invalid pages)
|
||||
this->updatePageUI();
|
||||
}
|
||||
|
||||
void ModList::showStatus(std::string const& status, bool loading) {
|
||||
// Clear list contents
|
||||
m_list->m_contentLayer->removeAllChildren();
|
||||
|
||||
// Update status
|
||||
m_statusText->setText(status);
|
||||
m_statusText->updateAnchoredPosition(Anchor::Center, (loading ? ccp(0, 40) : ccp(0, 0)));
|
||||
|
||||
// Make status visible
|
||||
m_statusText->setVisible(true);
|
||||
m_statusLoadingCircle->setVisible(loading);
|
||||
}
|
||||
|
||||
ModList* ModList::create(ModListSource* src, CCSize const& size) {
|
||||
auto ret = new ModList();
|
||||
if (ret && ret->init(src, size)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
CC_SAFE_DELETE(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ModsLayer::init() {
|
||||
if (!CCLayer::init())
|
||||
return false;
|
||||
|
@ -49,54 +244,45 @@ bool ModsLayer::init() {
|
|||
);
|
||||
this->addChildAtPosition(actionsMenu, Anchor::BottomLeft, ccp(35, 12), false);
|
||||
|
||||
auto frame = CCNode::create();
|
||||
frame->setAnchorPoint({ .5f, .5f });
|
||||
frame->setContentSize({ 380, 205 });
|
||||
m_frame = CCNode::create();
|
||||
m_frame->setAnchorPoint({ .5f, .5f });
|
||||
m_frame->setContentSize({ 380, 205 });
|
||||
|
||||
auto frameBG = CCLayerColor::create({ 25, 17, 37, 255 });
|
||||
frameBG->setContentSize(frame->getContentSize());
|
||||
frameBG->setContentSize(m_frame->getContentSize());
|
||||
frameBG->ignoreAnchorPointForPosition(false);
|
||||
frame->addChildAtPosition(frameBG, Anchor::Center);
|
||||
m_frame->addChildAtPosition(frameBG, Anchor::Center);
|
||||
|
||||
auto tabsTop = CCSprite::createWithSpriteFrameName("mods-list-top.png"_spr);
|
||||
tabsTop->setAnchorPoint({ .5f, .0f });
|
||||
frame->addChildAtPosition(tabsTop, Anchor::Top, ccp(0, -2));
|
||||
m_frame->addChildAtPosition(tabsTop, Anchor::Top, ccp(0, -2));
|
||||
|
||||
auto tabsLeft = CCSprite::createWithSpriteFrameName("mods-list-side.png"_spr);
|
||||
tabsLeft->setScaleY(frame->getContentHeight() / tabsLeft->getContentHeight());
|
||||
frame->addChildAtPosition(tabsLeft, Anchor::Left, ccp(6, 0));
|
||||
tabsLeft->setScaleY(m_frame->getContentHeight() / tabsLeft->getContentHeight());
|
||||
m_frame->addChildAtPosition(tabsLeft, Anchor::Left, ccp(6, 0));
|
||||
|
||||
auto tabsRight = CCSprite::createWithSpriteFrameName("mods-list-side.png"_spr);
|
||||
tabsRight->setFlipX(true);
|
||||
tabsRight->setScaleY(frame->getContentHeight() / tabsRight->getContentHeight());
|
||||
frame->addChildAtPosition(tabsRight, Anchor::Right, ccp(-6, 0));
|
||||
tabsRight->setScaleY(m_frame->getContentHeight() / tabsRight->getContentHeight());
|
||||
m_frame->addChildAtPosition(tabsRight, Anchor::Right, ccp(-6, 0));
|
||||
|
||||
auto tabsBottom = CCSprite::createWithSpriteFrameName("mods-list-bottom.png"_spr);
|
||||
tabsBottom->setAnchorPoint({ .5f, 1.f });
|
||||
frame->addChildAtPosition(tabsBottom, Anchor::Bottom, ccp(0, 2));
|
||||
m_frame->addChildAtPosition(tabsBottom, Anchor::Bottom, ccp(0, 2));
|
||||
|
||||
this->addChildAtPosition(frame, Anchor::Center, ccp(0, -10), false);
|
||||
|
||||
m_list = ScrollLayer::create(frame->getContentSize() - ccp(24, 0));
|
||||
m_list->m_contentLayer->setLayout(
|
||||
ColumnLayout::create()
|
||||
->setAxisReverse(true)
|
||||
->setAxisAlignment(AxisAlignment::End)
|
||||
->setAutoGrowAxis(frame->getContentHeight())
|
||||
);
|
||||
this->addChildAtPosition(m_list, Anchor::Center, -m_list->getScaledContentSize() / 2 - ccp(0, 10), false);
|
||||
this->addChildAtPosition(m_frame, Anchor::Center, ccp(0, -10), false);
|
||||
|
||||
auto mainTabs = CCMenu::create();
|
||||
mainTabs->setContentWidth(tabsTop->getContentWidth() - 45);
|
||||
mainTabs->setAnchorPoint({ .5f, .0f });
|
||||
mainTabs->setPosition(frame->convertToWorldSpace(tabsTop->getPosition() + ccp(0, 10)));
|
||||
mainTabs->setPosition(m_frame->convertToWorldSpace(tabsTop->getPosition() + ccp(0, 10)));
|
||||
|
||||
for (auto item : std::initializer_list<std::tuple<const char*, const char*, const char*>> {
|
||||
{ "download.png"_spr, "Installed", "installed" },
|
||||
{ "GJ_bigStar_noShadow_001.png", "Featured", "featured" },
|
||||
{ "GJ_sTrendingIcon_001.png", "Trending", "trending" },
|
||||
{ "gj_folderBtn_001.png", "Mod Packs", "mod-packs" },
|
||||
{ "search.png"_spr, "Search", "search" },
|
||||
for (auto item : std::initializer_list<std::tuple<const char*, const char*, ModListSource*>> {
|
||||
{ "download.png"_spr, "Installed", InstalledModsList::get() },
|
||||
{ "GJ_bigStar_noShadow_001.png", "Featured", nullptr },
|
||||
{ "GJ_sTrendingIcon_001.png", "Trending", nullptr },
|
||||
{ "gj_folderBtn_001.png", "Mod Packs", ModPacksModsList::get() },
|
||||
{ "search.png"_spr, "Search", nullptr },
|
||||
}) {
|
||||
const CCSize itemSize { 100, 35 };
|
||||
const CCSize iconSize { 18, 18 };
|
||||
|
@ -129,7 +315,7 @@ bool ModsLayer::init() {
|
|||
spr->addChildAtPosition(title, Anchor::Left, ccp(28, 0), false);
|
||||
|
||||
auto btn = CCMenuItemSpriteExtra::create(spr, this, menu_selector(ModsLayer::onTab));
|
||||
btn->setID(std::get<2>(item));
|
||||
btn->setUserData(std::get<2>(item));
|
||||
mainTabs->addChild(btn);
|
||||
m_tabs.push_back(btn);
|
||||
}
|
||||
|
@ -137,7 +323,7 @@ bool ModsLayer::init() {
|
|||
mainTabs->setLayout(RowLayout::create());
|
||||
this->addChild(mainTabs);
|
||||
|
||||
this->gotoTab("installed");
|
||||
this->gotoTab();
|
||||
|
||||
this->setKeypadEnabled(true);
|
||||
cocos::handleTouchPriority(this, true);
|
||||
|
@ -145,65 +331,41 @@ bool ModsLayer::init() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ModsLayer::loadList(std::string const& id) {
|
||||
if (!m_currentListID.empty()) {
|
||||
m_listPosCaches[m_currentListID].scrollPosition = m_list->m_contentLayer->getPositionY();
|
||||
m_listPosCaches[m_currentListID].page = m_currentPage;
|
||||
void ModsLayer::gotoTab(ModListSource* src) {
|
||||
// Default to installed mods
|
||||
if (!src) {
|
||||
src = InstalledModsList::get();
|
||||
}
|
||||
if (!m_listPosCaches.contains(id)) {
|
||||
m_listPosCaches.insert_or_assign(id, ListPosCache {
|
||||
.page = 0,
|
||||
.scrollPosition = std::numeric_limits<float>::max(),
|
||||
});
|
||||
}
|
||||
m_currentListID = id;
|
||||
if (auto srcOpt = ModListSource::get(id)) {
|
||||
auto& src = srcOpt.value().get();
|
||||
src.loadTotalPageCount()
|
||||
.then([this](size_t count) {
|
||||
// todo: show count in UI
|
||||
this->loadPage(m_listPosCaches.at(id).page);
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ModsLayer::loadPage(size_t page, bool update) {
|
||||
m_list->m_contentLayer->removeAllChildren();
|
||||
if (auto srcOpt = ModListSource::get(m_currentListID)) {
|
||||
auto& src = srcOpt.value().get();
|
||||
src.loadNewPage();
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
for (auto item : cache.source.loadNewPage(m_currentPage, update)) {
|
||||
m_list->m_contentLayer->addChild(item);
|
||||
item->updateSize(m_list->getContentWidth(), BIG_VIEW);
|
||||
}
|
||||
m_list->m_contentLayer->updateLayout();
|
||||
auto listTopScrollPos = -m_list->m_contentLayer->getContentHeight() + m_list->getContentHeight();
|
||||
if (cache.scrollPosition > 0.f || cache.scrollPosition < listTopScrollPos) {
|
||||
cache.scrollPosition = listTopScrollPos;
|
||||
}
|
||||
m_list->m_contentLayer->setPositionY(cache.scrollPosition);
|
||||
m_currentList = &cache;
|
||||
}
|
||||
|
||||
void ModsLayer::gotoTab(std::string const& id) {
|
||||
// Update selected tab
|
||||
for (auto tab : m_tabs) {
|
||||
auto selected = tab->getID() == id;
|
||||
auto selected = tab->getUserData() == src;
|
||||
tab->getNormalImage()->getChildByID("disabled-bg")->setVisible(!selected);
|
||||
tab->getNormalImage()->getChildByID("enabled-bg")->setVisible(selected);
|
||||
tab->setEnabled(!selected);
|
||||
}
|
||||
this->loadList(id);
|
||||
// Remove current list from UI (it's Ref'd so it stays in memory)
|
||||
if (m_currentSource) {
|
||||
m_lists.at(m_currentSource)->removeFromParent();
|
||||
}
|
||||
// Update current source
|
||||
m_currentSource = src;
|
||||
|
||||
// Lazily create new list and add it to UI
|
||||
if (!m_lists.contains(src)) {
|
||||
auto list = ModList::create(src, m_frame->getContentSize() - ccp(24, 0));
|
||||
list->setPosition(m_frame->getPosition());
|
||||
this->addChild(list);
|
||||
m_lists.emplace(src, list);
|
||||
}
|
||||
// Add list to UI
|
||||
else {
|
||||
this->addChild(m_lists.at(src));
|
||||
}
|
||||
}
|
||||
|
||||
void ModsLayer::onTab(CCObject* sender) {
|
||||
this->gotoTab(static_cast<CCNode*>(sender)->getID());
|
||||
this->gotoTab(static_cast<ModListSource*>(static_cast<CCNode*>(sender)->getUserData()));
|
||||
}
|
||||
|
||||
void ModsLayer::keyBackClicked() {
|
||||
|
@ -211,9 +373,7 @@ void ModsLayer::keyBackClicked() {
|
|||
}
|
||||
|
||||
void ModsLayer::onRefreshList(CCObject*) {
|
||||
if (m_currentList) {
|
||||
this->loadList(m_currentList->id, true);
|
||||
}
|
||||
m_lists.at(m_currentSource)->reloadPage();
|
||||
}
|
||||
|
||||
void ModsLayer::onBack(CCObject*) {
|
||||
|
|
|
@ -2,32 +2,50 @@
|
|||
|
||||
#include <Geode/ui/General.hpp>
|
||||
#include <Geode/ui/ScrollLayer.hpp>
|
||||
#include <Geode/ui/TextArea.hpp>
|
||||
#include "ModItem.hpp"
|
||||
#include "ModListSource.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
// Stores the current page and scroll position for a given list
|
||||
struct ListPosCache {
|
||||
size_t page;
|
||||
float scrollPosition;
|
||||
class ModList : public CCNode {
|
||||
protected:
|
||||
ModListSource* m_source;
|
||||
size_t m_page = 0;
|
||||
ScrollLayer* m_list;
|
||||
SimpleTextArea* m_statusText;
|
||||
CCSprite* m_statusLoadingCircle;
|
||||
ModListSource::PageLoadEventListener m_listener;
|
||||
CCMenuItemSpriteExtra* m_pagePrevBtn;
|
||||
CCMenuItemSpriteExtra* m_pageNextBtn;
|
||||
CCLabelBMFont* m_pageLabel;
|
||||
|
||||
bool init(ModListSource* src, CCSize const& size);
|
||||
|
||||
void onPromise(PromiseEvent<ModListSource::Page>* event);
|
||||
void onPage(CCObject*);
|
||||
|
||||
void updatePageUI(bool hide = false);
|
||||
|
||||
public:
|
||||
static ModList* create(ModListSource* src, CCSize const& size);
|
||||
|
||||
void reloadPage();
|
||||
void gotoPage(size_t page, bool update = false);
|
||||
void showStatus(std::string const& status, bool loading);
|
||||
};
|
||||
|
||||
class ModsLayer : public CCLayer {
|
||||
protected:
|
||||
CCNode* m_frame;
|
||||
std::vector<CCMenuItemSpriteExtra*> m_tabs;
|
||||
ScrollLayer* m_list;
|
||||
std::string m_currentListID;
|
||||
size_t m_currentPage = 0;
|
||||
std::unordered_map<std::string, ListPosCache> m_listPosCaches;
|
||||
ModListSource* m_currentSource = nullptr;
|
||||
std::unordered_map<ModListSource*, Ref<ModList>> m_lists;
|
||||
|
||||
bool init();
|
||||
|
||||
void keyBackClicked() override;
|
||||
void onTab(CCObject* sender);
|
||||
void gotoTab(std::string const& id);
|
||||
void loadList(std::string const& id);
|
||||
void loadPage(size_t page, bool update = false);
|
||||
|
||||
public:
|
||||
static ModsLayer* create();
|
||||
|
@ -35,4 +53,6 @@ public:
|
|||
|
||||
void onBack(CCObject*);
|
||||
void onRefreshList(CCObject*);
|
||||
|
||||
void gotoTab(ModListSource* src = nullptr);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue