mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-27 01:45:35 -05:00
Compare commits
4 commits
468dea6346
...
c06c5aff25
Author | SHA1 | Date | |
---|---|---|---|
|
c06c5aff25 | ||
|
7bcf50da57 | ||
|
38f3385c90 | ||
|
4d5e465ade |
11 changed files with 372 additions and 143 deletions
|
@ -71,6 +71,7 @@ namespace geode::utils {
|
||||||
std::string m_msg;
|
std::string m_msg;
|
||||||
Timer<Clock> m_timer;
|
Timer<Clock> m_timer;
|
||||||
|
|
||||||
|
// @geode-ignore(geode-alternative)
|
||||||
LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) :
|
LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) :
|
||||||
m_msg(msg), m_output(out) {
|
m_msg(msg), m_output(out) {
|
||||||
m_timer = Timer<Clock>();
|
m_timer = Timer<Clock>();
|
||||||
|
|
BIN
loader/resources/grid-view.png
Normal file
BIN
loader/resources/grid-view.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -10,11 +10,13 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
|
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
|
||||||
|
// @geode-ignore(geode-alternative)
|
||||||
std::cout << title << ": " << info << std::endl;
|
std::cout << title << ": " << info << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::logConsoleMessageWithSeverity(std::string const& msg, Severity severity) {
|
void Loader::Impl::logConsoleMessageWithSeverity(std::string const& msg, Severity severity) {
|
||||||
if (m_platformConsoleOpen) {
|
if (m_platformConsoleOpen) {
|
||||||
|
// @geode-ignore(geode-alternative)
|
||||||
std::cout << msg << "\n" << std::flush;
|
std::cout << msg << "\n" << std::flush;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ enum {
|
||||||
ID_BUTTON_OPEN_FOLDER = 103,
|
ID_BUTTON_OPEN_FOLDER = 103,
|
||||||
ID_BUTTON_COPY_CLIPBOARD = 104,
|
ID_BUTTON_COPY_CLIPBOARD = 104,
|
||||||
ID_BUTTON_RESTART_GAME = 105,
|
ID_BUTTON_RESTART_GAME = 105,
|
||||||
|
ID_SAFE_MODE_TIP_TEXT = 106,
|
||||||
};
|
};
|
||||||
#define TO_HMENU(x) reinterpret_cast<HMENU>(static_cast<size_t>(x))
|
#define TO_HMENU(x) reinterpret_cast<HMENU>(static_cast<size_t>(x))
|
||||||
|
|
||||||
|
@ -63,6 +64,25 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
DEFAULT_PITCH | FF_DONTCARE, TEXT("Consolas"));
|
DEFAULT_PITCH | FF_DONTCARE, TEXT("Consolas"));
|
||||||
auto guiFont = static_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
|
auto guiFont = static_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
|
||||||
|
|
||||||
|
auto calculateTextSize = [&](std::string_view text) -> SIZE {
|
||||||
|
HDC hdc = GetDC(hwnd);
|
||||||
|
SelectObject(hdc, monoFont);
|
||||||
|
SIZE size;
|
||||||
|
GetTextExtentPoint32A(hdc, text.data(), text.size(), &size);
|
||||||
|
ReleaseDC(hwnd, hdc);
|
||||||
|
return size;
|
||||||
|
};
|
||||||
|
auto tipTextStr = "Tip: You can hold shift while launching the game to enter safe mode.";
|
||||||
|
auto tipTextSize = calculateTextSize(tipTextStr);
|
||||||
|
|
||||||
|
auto tipText = CreateWindowA(
|
||||||
|
"STATIC", tipTextStr,
|
||||||
|
WS_CHILD | WS_VISIBLE | SS_SIMPLE,
|
||||||
|
0, 0, tipTextSize.cx, tipTextSize.cy,
|
||||||
|
hwnd, TO_HMENU(ID_SAFE_MODE_TIP_TEXT), NULL, NULL
|
||||||
|
);
|
||||||
|
SendMessage(tipText, WM_SETFONT, WPARAM(monoFont), TRUE);
|
||||||
|
|
||||||
auto handleText = CreateWindowA(
|
auto handleText = CreateWindowA(
|
||||||
"EDIT", "Crashlog text goes here", WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_BORDER,
|
"EDIT", "Crashlog text goes here", WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_BORDER,
|
||||||
0, 0, 100, 100,
|
0, 0, 100, 100,
|
||||||
|
@ -109,10 +129,20 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
RECT clientRect;
|
RECT clientRect;
|
||||||
GetClientRect(hwnd, &clientRect);
|
GetClientRect(hwnd, &clientRect);
|
||||||
|
|
||||||
|
RECT textRect;
|
||||||
|
GetClientRect(GetDlgItem(hwnd, ID_SAFE_MODE_TIP_TEXT), &textRect);
|
||||||
|
|
||||||
|
SetWindowPos(
|
||||||
|
GetDlgItem(hwnd, ID_SAFE_MODE_TIP_TEXT), NULL,
|
||||||
|
layout::PADDING, layout::PADDING,
|
||||||
|
0, 0,
|
||||||
|
SWP_NOZORDER | SWP_NOSIZE
|
||||||
|
);
|
||||||
|
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
GetDlgItem(hwnd, ID_CRASHLOG_TEXT), NULL,
|
GetDlgItem(hwnd, ID_CRASHLOG_TEXT), NULL,
|
||||||
layout::PADDING, layout::PADDING,
|
layout::PADDING, layout::PADDING * 2 + textRect.bottom,
|
||||||
clientRect.right - layout::PADDING * 2, clientRect.bottom - layout::BUTTON_HEIGHT - layout::PADDING * 3,
|
clientRect.right - layout::PADDING * 2, clientRect.bottom - layout::BUTTON_HEIGHT - layout::PADDING * 4 - textRect.bottom,
|
||||||
SWP_NOZORDER
|
SWP_NOZORDER
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -150,6 +180,9 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case WM_CTLCOLORSTATIC: {
|
case WM_CTLCOLORSTATIC: {
|
||||||
|
auto hdc = (HDC)wParam;
|
||||||
|
// make every text have transparent background
|
||||||
|
SetBkMode(hdc, TRANSPARENT);
|
||||||
return (LRESULT)(COLOR_WINDOWFRAME);
|
return (LRESULT)(COLOR_WINDOWFRAME);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
@ -499,28 +499,48 @@ bool ModsLayer::init() {
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
auto listActionsMenu = CCMenu::create();
|
auto listDisplayMenu = CCMenu::create();
|
||||||
listActionsMenu->setID("list-actions-menu");
|
listDisplayMenu->setID("list-actions-menu");
|
||||||
listActionsMenu->setContentHeight(100);
|
listDisplayMenu->setContentHeight(100);
|
||||||
listActionsMenu->setAnchorPoint({ 1, 0 });
|
listDisplayMenu->setAnchorPoint({ 1, 0 });
|
||||||
listActionsMenu->setScale(.65f);
|
listDisplayMenu->setScale(.65f);
|
||||||
|
|
||||||
|
auto smallSizeBtn = CCMenuItemSpriteExtra::create(
|
||||||
|
GeodeSquareSprite::createWithSpriteFrameName("GJ_smallModeIcon_001.png"),
|
||||||
|
this, menu_selector(ModsLayer::onDisplay)
|
||||||
|
);
|
||||||
|
smallSizeBtn->setTag(static_cast<int>(ModListDisplay::SmallList));
|
||||||
|
smallSizeBtn->setID("list-normal-size-button");
|
||||||
|
listDisplayMenu->addChild(smallSizeBtn);
|
||||||
|
m_displayBtns.push_back(smallSizeBtn);
|
||||||
|
|
||||||
auto bigSizeBtn = CCMenuItemSpriteExtra::create(
|
auto bigSizeBtn = CCMenuItemSpriteExtra::create(
|
||||||
GeodeSquareSprite::createWithSpriteFrameName("GJ_smallModeIcon_001.png", &m_bigView),
|
GeodeSquareSprite::createWithSpriteFrameName("GJ_extendedIcon_001.png"),
|
||||||
this, menu_selector(ModsLayer::onBigView)
|
this, menu_selector(ModsLayer::onDisplay)
|
||||||
);
|
);
|
||||||
|
bigSizeBtn->setTag(static_cast<int>(ModListDisplay::BigList));
|
||||||
bigSizeBtn->setID("list-size-button");
|
bigSizeBtn->setID("list-size-button");
|
||||||
listActionsMenu->addChild(bigSizeBtn);
|
listDisplayMenu->addChild(bigSizeBtn);
|
||||||
|
m_displayBtns.push_back(bigSizeBtn);
|
||||||
|
|
||||||
|
auto gridBtn = CCMenuItemSpriteExtra::create(
|
||||||
|
GeodeSquareSprite::createWithSpriteFrameName("grid-view.png"_spr),
|
||||||
|
this, menu_selector(ModsLayer::onDisplay)
|
||||||
|
);
|
||||||
|
gridBtn->setTag(static_cast<int>(ModListDisplay::Grid));
|
||||||
|
gridBtn->setID("list-size-button");
|
||||||
|
listDisplayMenu->addChild(gridBtn);
|
||||||
|
m_displayBtns.push_back(gridBtn);
|
||||||
|
|
||||||
// auto searchBtn = CCMenuItemSpriteExtra::create(
|
// auto searchBtn = CCMenuItemSpriteExtra::create(
|
||||||
// GeodeSquareSprite::createWithSpriteFrameName("search.png"_spr, &m_showSearch),
|
// GeodeSquareSprite::createWithSpriteFrameName("search.png"_spr, &m_showSearch),
|
||||||
// this, menu_selector(ModsLayer::onSearch)
|
// this, menu_selector(ModsLayer::onSearch)
|
||||||
// );
|
// );
|
||||||
// searchBtn->setID("search-button");
|
// searchBtn->setID("search-button");
|
||||||
// listActionsMenu->addChild(searchBtn);
|
// listDisplayMenu->addChild(searchBtn);
|
||||||
|
|
||||||
listActionsMenu->setLayout(ColumnLayout::create());
|
listDisplayMenu->setLayout(ColumnLayout::create()->setAxisReverse(true));
|
||||||
m_frame->addChildAtPosition(listActionsMenu, Anchor::Left, ccp(-5, 25));
|
m_frame->addChildAtPosition(listDisplayMenu, Anchor::Left, ccp(-5, 25));
|
||||||
|
|
||||||
m_statusNode = ModsStatusNode::create();
|
m_statusNode = ModsStatusNode::create();
|
||||||
m_statusNode->setZOrder(4);
|
m_statusNode->setZOrder(4);
|
||||||
|
@ -629,7 +649,7 @@ void ModsLayer::gotoTab(ModListSource* src) {
|
||||||
m_currentSource = src;
|
m_currentSource = src;
|
||||||
|
|
||||||
// Update the state of the current list
|
// Update the state of the current list
|
||||||
m_lists.at(m_currentSource)->updateSize(m_bigView);
|
m_lists.at(m_currentSource)->updateDisplay(m_display);
|
||||||
m_lists.at(m_currentSource)->activateSearch(m_showSearch);
|
m_lists.at(m_currentSource)->activateSearch(m_showSearch);
|
||||||
m_lists.at(m_currentSource)->updateState();
|
m_lists.at(m_currentSource)->updateState();
|
||||||
}
|
}
|
||||||
|
@ -687,6 +707,13 @@ void ModsLayer::updateState() {
|
||||||
else {
|
else {
|
||||||
m_pageMenu->setVisible(false);
|
m_pageMenu->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update display button
|
||||||
|
for (auto btn : m_displayBtns) {
|
||||||
|
static_cast<GeodeSquareSprite*>(btn->getNormalImage())->setState(
|
||||||
|
static_cast<ModListDisplay>(btn->getTag()) == m_display
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModsLayer::onTab(CCObject* sender) {
|
void ModsLayer::onTab(CCObject* sender) {
|
||||||
|
@ -716,12 +743,13 @@ void ModsLayer::onGoToPage(CCObject*) {
|
||||||
popup->setID("go-to-page"_spr);
|
popup->setID("go-to-page"_spr);
|
||||||
popup->show();
|
popup->show();
|
||||||
}
|
}
|
||||||
void ModsLayer::onBigView(CCObject*) {
|
void ModsLayer::onDisplay(CCObject* sender) {
|
||||||
m_bigView = !m_bigView;
|
m_display = static_cast<ModListDisplay>(sender->getTag());
|
||||||
// Make sure to avoid a crash
|
// Make sure to avoid a crash
|
||||||
if (m_currentSource) {
|
if (m_currentSource) {
|
||||||
m_lists.at(m_currentSource)->updateSize(m_bigView);
|
m_lists.at(m_currentSource)->updateDisplay(m_display);
|
||||||
}
|
}
|
||||||
|
this->updateState();
|
||||||
}
|
}
|
||||||
void ModsLayer::onSearch(CCObject*) {
|
void ModsLayer::onSearch(CCObject*) {
|
||||||
m_showSearch = !m_showSearch;
|
m_showSearch = !m_showSearch;
|
||||||
|
|
|
@ -66,7 +66,8 @@ protected:
|
||||||
ModsStatusNode* m_statusNode;
|
ModsStatusNode* m_statusNode;
|
||||||
EventListener<UpdateModListStateFilter> m_updateStateListener;
|
EventListener<UpdateModListStateFilter> m_updateStateListener;
|
||||||
bool m_showSearch = true;
|
bool m_showSearch = true;
|
||||||
bool m_bigView = false;
|
ModListDisplay m_display = ModListDisplay::SmallList;
|
||||||
|
std::vector<CCMenuItemSpriteExtra*> m_displayBtns;
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ protected:
|
||||||
void onTab(CCObject* sender);
|
void onTab(CCObject* sender);
|
||||||
void onOpenModsFolder(CCObject*);
|
void onOpenModsFolder(CCObject*);
|
||||||
void onAddModFromFile(CCObject*);
|
void onAddModFromFile(CCObject*);
|
||||||
void onBigView(CCObject*);
|
void onDisplay(CCObject*);
|
||||||
void onSearch(CCObject*);
|
void onSearch(CCObject*);
|
||||||
void onGoToPage(CCObject*);
|
void onGoToPage(CCObject*);
|
||||||
void onRefreshList(CCObject*);
|
void onRefreshList(CCObject*);
|
||||||
|
|
|
@ -29,7 +29,7 @@ bool ModItem::init(ModSource&& source) {
|
||||||
m_bg->ignoreAnchorPointForPosition(false);
|
m_bg->ignoreAnchorPointForPosition(false);
|
||||||
m_bg->setAnchorPoint({ .5f, .5f });
|
m_bg->setAnchorPoint({ .5f, .5f });
|
||||||
m_bg->setScale(.7f);
|
m_bg->setScale(.7f);
|
||||||
this->addChild(m_bg);
|
this->addChildAtPosition(m_bg, Anchor::Center);
|
||||||
|
|
||||||
m_logo = m_source.createModLogo();
|
m_logo = m_source.createModLogo();
|
||||||
m_logo->setID("logo-sprite");
|
m_logo->setID("logo-sprite");
|
||||||
|
@ -39,23 +39,10 @@ bool ModItem::init(ModSource&& source) {
|
||||||
m_infoContainer->setID("info-container");
|
m_infoContainer->setID("info-container");
|
||||||
m_infoContainer->setScale(.4f);
|
m_infoContainer->setScale(.4f);
|
||||||
m_infoContainer->setAnchorPoint({ .0f, .5f });
|
m_infoContainer->setAnchorPoint({ .0f, .5f });
|
||||||
m_infoContainer->setLayout(
|
|
||||||
ColumnLayout::create()
|
|
||||||
->setAxisReverse(true)
|
|
||||||
->setAxisAlignment(AxisAlignment::Even)
|
|
||||||
->setCrossAxisLineAlignment(AxisAlignment::Start)
|
|
||||||
->setGap(0)
|
|
||||||
);
|
|
||||||
m_infoContainer->getLayout()->ignoreInvisibleChildren(true);
|
|
||||||
|
|
||||||
m_titleContainer = CCNode::create();
|
m_titleContainer = CCNode::create();
|
||||||
m_titleContainer->setID("title-container");
|
m_titleContainer->setID("title-container");
|
||||||
m_titleContainer->setAnchorPoint({ .0f, .5f });
|
m_titleContainer->setAnchorPoint({ .0f, .5f });
|
||||||
m_titleContainer->setLayout(
|
|
||||||
RowLayout::create()
|
|
||||||
->setDefaultScaleLimits(.1f, 1.f)
|
|
||||||
->setAxisAlignment(AxisAlignment::Start)
|
|
||||||
);
|
|
||||||
|
|
||||||
m_titleLabel = CCLabelBMFont::create(m_source.getMetadata().getName().c_str(), "bigFont.fnt");
|
m_titleLabel = CCLabelBMFont::create(m_source.getMetadata().getName().c_str(), "bigFont.fnt");
|
||||||
m_titleLabel->setID("title-label");
|
m_titleLabel->setID("title-label");
|
||||||
|
@ -67,14 +54,20 @@ bool ModItem::init(ModSource&& source) {
|
||||||
m_versionLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .7f));
|
m_versionLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .7f));
|
||||||
m_titleContainer->addChild(m_versionLabel);
|
m_titleContainer->addChild(m_versionLabel);
|
||||||
|
|
||||||
m_infoContainer->addChild(m_titleContainer);
|
m_titleContainer->setLayout(
|
||||||
|
RowLayout::create()
|
||||||
|
->setDefaultScaleLimits(.1f, 1.f)
|
||||||
|
->setAxisAlignment(AxisAlignment::Start)
|
||||||
|
);
|
||||||
|
m_titleContainer->getLayout()->ignoreInvisibleChildren(true);
|
||||||
|
m_infoContainer->addChildAtPosition(m_titleContainer, Anchor::Left);
|
||||||
|
|
||||||
m_developers = CCMenu::create();
|
m_developers = CCMenu::create();
|
||||||
m_developers->setID("developers-menu");
|
m_developers->setID("developers-menu");
|
||||||
m_developers->ignoreAnchorPointForPosition(false);
|
m_developers->ignoreAnchorPointForPosition(false);
|
||||||
m_developers->setAnchorPoint({ .0f, .5f });
|
m_developers->setAnchorPoint({ .0f, .5f });
|
||||||
|
|
||||||
auto by = "By " + m_source.formatDevelopers();
|
auto by = m_source.formatDevelopers();
|
||||||
m_developerLabel = CCLabelBMFont::create(by.c_str(), "goldFont.fnt");
|
m_developerLabel = CCLabelBMFont::create(by.c_str(), "goldFont.fnt");
|
||||||
m_developerLabel->setID("developers-label");
|
m_developerLabel->setID("developers-label");
|
||||||
auto developersBtn = CCMenuItemSpriteExtra::create(
|
auto developersBtn = CCMenuItemSpriteExtra::create(
|
||||||
|
@ -87,7 +80,24 @@ bool ModItem::init(ModSource&& source) {
|
||||||
RowLayout::create()
|
RowLayout::create()
|
||||||
->setAxisAlignment(AxisAlignment::Start)
|
->setAxisAlignment(AxisAlignment::Start)
|
||||||
);
|
);
|
||||||
m_infoContainer->addChild(m_developers);
|
m_infoContainer->addChildAtPosition(m_developers, Anchor::Left);
|
||||||
|
|
||||||
|
m_description = CCScale9Sprite::create("square02b_001.png");
|
||||||
|
m_description->setScale(.5f);
|
||||||
|
m_description->setContentSize(ccp(450, 30) / m_description->getScale());
|
||||||
|
m_description->setColor(ccBLACK);
|
||||||
|
m_description->setOpacity(90);
|
||||||
|
|
||||||
|
auto desc = m_source.getMetadata().getDescription();
|
||||||
|
auto descLabel = CCLabelBMFont::create(
|
||||||
|
desc.value_or("[No Description Provided]").c_str(),
|
||||||
|
"chatFont.fnt"
|
||||||
|
);
|
||||||
|
descLabel->setColor(desc ? ccWHITE : ccGRAY);
|
||||||
|
limitNodeWidth(descLabel, m_description->getContentWidth() - 20, 2.f, .1f);
|
||||||
|
m_description->addChildAtPosition(descLabel, Anchor::Left, ccp(10, 0), ccp(0, .5f));
|
||||||
|
|
||||||
|
m_infoContainer->addChildAtPosition(m_description, Anchor::Left);
|
||||||
|
|
||||||
m_restartRequiredLabel = createTagLabel(
|
m_restartRequiredLabel = createTagLabel(
|
||||||
"Restart Required",
|
"Restart Required",
|
||||||
|
@ -97,19 +107,19 @@ bool ModItem::init(ModSource&& source) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
m_restartRequiredLabel->setID("restart-required-label");
|
m_restartRequiredLabel->setID("restart-required-label");
|
||||||
m_restartRequiredLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
|
m_restartRequiredLabel->setScale(.75f);
|
||||||
m_infoContainer->addChild(m_restartRequiredLabel);
|
m_infoContainer->addChildAtPosition(m_restartRequiredLabel, Anchor::Left);
|
||||||
|
|
||||||
m_outdatedLabel = createTagLabel(
|
m_outdatedLabel = createTagLabel(
|
||||||
fmt::format("Outdated (GD {})", m_source.getMetadata().getGameVersion().value_or("*")),
|
"Outdated",
|
||||||
{
|
{
|
||||||
to3B(ColorProvider::get()->color("mod-list-outdated-label"_spr)),
|
to3B(ColorProvider::get()->color("mod-list-outdated-label"_spr)),
|
||||||
to3B(ColorProvider::get()->color("mod-list-outdated-label-bg"_spr))
|
to3B(ColorProvider::get()->color("mod-list-outdated-label-bg"_spr))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
m_outdatedLabel->setID("outdated-label");
|
m_outdatedLabel->setID("outdated-label");
|
||||||
m_outdatedLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
|
m_outdatedLabel->setScale(.75f);
|
||||||
m_infoContainer->addChild(m_outdatedLabel);
|
m_infoContainer->addChildAtPosition(m_outdatedLabel, Anchor::Left);
|
||||||
|
|
||||||
m_downloadBarContainer = CCNode::create();
|
m_downloadBarContainer = CCNode::create();
|
||||||
m_downloadBarContainer->setID("download-bar-container");
|
m_downloadBarContainer->setID("download-bar-container");
|
||||||
|
@ -121,7 +131,7 @@ bool ModItem::init(ModSource&& source) {
|
||||||
m_downloadBar->setScale(1.5f);
|
m_downloadBar->setScale(1.5f);
|
||||||
m_downloadBarContainer->addChildAtPosition(m_downloadBar, Anchor::Center, ccp(0, 0), ccp(0, 0));
|
m_downloadBarContainer->addChildAtPosition(m_downloadBar, Anchor::Center, ccp(0, 0), ccp(0, 0));
|
||||||
|
|
||||||
m_infoContainer->addChild(m_downloadBarContainer);
|
m_infoContainer->addChildAtPosition(m_downloadBarContainer, Anchor::Left);
|
||||||
|
|
||||||
m_downloadWaiting = CCNode::create();
|
m_downloadWaiting = CCNode::create();
|
||||||
m_downloadWaiting->setID("download-waiting-container");
|
m_downloadWaiting->setID("download-waiting-container");
|
||||||
|
@ -141,13 +151,12 @@ bool ModItem::init(ModSource&& source) {
|
||||||
ccp(m_downloadWaiting->getContentHeight() / 2, 0)
|
ccp(m_downloadWaiting->getContentHeight() / 2, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
m_infoContainer->addChild(m_downloadWaiting);
|
m_infoContainer->addChildAtPosition(m_downloadWaiting, Anchor::Left);
|
||||||
|
|
||||||
this->addChild(m_infoContainer);
|
this->addChildAtPosition(m_infoContainer, Anchor::Left);
|
||||||
|
|
||||||
m_viewMenu = CCMenu::create();
|
m_viewMenu = CCMenu::create();
|
||||||
m_viewMenu->setID("view-menu");
|
m_viewMenu->setID("view-menu");
|
||||||
m_viewMenu->setAnchorPoint({ 1.f, .5f });
|
|
||||||
m_viewMenu->setScale(.55f);
|
m_viewMenu->setScale(.55f);
|
||||||
|
|
||||||
ButtonSprite* spr = nullptr;
|
ButtonSprite* spr = nullptr;
|
||||||
|
@ -214,43 +223,38 @@ bool ModItem::init(ModSource&& source) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[this](server::ServerModMetadata const& metadata) {
|
[this](server::ServerModMetadata const& metadata) {
|
||||||
|
m_badgeContainer = CCNode::create();
|
||||||
|
m_badgeContainer->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
||||||
if (metadata.featured) {
|
if (metadata.featured) {
|
||||||
auto star = CCSprite::createWithSpriteFrameName("tag-featured.png"_spr);
|
m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-featured.png"_spr));
|
||||||
star->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
|
||||||
m_titleContainer->addChild(star);
|
|
||||||
}
|
}
|
||||||
if (metadata.tags.contains("paid")) {
|
if (metadata.tags.contains("paid")) {
|
||||||
auto paidModLabel = CCSprite::createWithSpriteFrameName("tag-paid.png"_spr);
|
m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-paid.png"_spr));
|
||||||
paidModLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
|
||||||
m_titleContainer->addChild(paidModLabel);
|
|
||||||
}
|
}
|
||||||
if (metadata.tags.contains("modtober24")) {
|
if (metadata.tags.contains("modtober24")) {
|
||||||
auto modtoberLabel = CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr);
|
m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr));
|
||||||
modtoberLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
|
||||||
m_titleContainer->addChild(modtoberLabel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show mod download count here already so people can make informed decisions
|
// Show mod download count here already so people can make informed decisions
|
||||||
// on which mods to install
|
// on which mods to install
|
||||||
auto downloadsContainer = CCNode::create();
|
m_downloadCountContainer = CCNode::create();
|
||||||
|
|
||||||
auto downloads = CCLabelBMFont::create(numToAbbreviatedString(metadata.downloadCount).c_str(), "bigFont.fnt");
|
auto downloads = CCLabelBMFont::create(numToAbbreviatedString(metadata.downloadCount).c_str(), "bigFont.fnt");
|
||||||
downloads->setID("downloads-label");
|
downloads->setID("downloads-label");
|
||||||
downloads->setColor("mod-list-version-label"_cc3b);
|
downloads->setColor("mod-list-version-label"_cc3b);
|
||||||
downloads->limitLabelWidth(80, .5f, .1f);
|
downloads->limitLabelWidth(80, .5f, .1f);
|
||||||
downloadsContainer->addChildAtPosition(downloads, Anchor::Right, ccp(-0, 0), ccp(1, .5f));
|
m_downloadCountContainer->addChildAtPosition(downloads, Anchor::Right, ccp(-0, 0), ccp(1, .5f));
|
||||||
|
|
||||||
auto downloadsIcon = CCSprite::createWithSpriteFrameName("GJ_downloadsIcon_001.png");
|
auto downloadsIcon = CCSprite::createWithSpriteFrameName("GJ_downloadsIcon_001.png");
|
||||||
downloadsIcon->setID("downloads-icon-sprite");
|
downloadsIcon->setID("downloads-icon-sprite");
|
||||||
downloadsIcon->setScale(.75f);
|
downloadsIcon->setScale(.75f);
|
||||||
downloadsContainer->addChildAtPosition(downloadsIcon, Anchor::Right, ccp(-downloads->getScaledContentWidth() - 10, 0));
|
m_downloadCountContainer->addChildAtPosition(downloadsIcon, Anchor::Left, ccp(5, 0));
|
||||||
|
|
||||||
downloadsContainer->setContentSize({
|
m_downloadCountContainer->setContentSize({
|
||||||
downloads->getScaledContentWidth() + 10 + downloadsIcon->getScaledContentWidth() + 10,
|
downloads->getScaledContentWidth() + downloadsIcon->getScaledContentWidth(),
|
||||||
25
|
25
|
||||||
});
|
});
|
||||||
downloadsContainer->updateLayout();
|
m_downloadCountContainer->updateLayout();
|
||||||
m_viewMenu->addChild(downloadsContainer);
|
|
||||||
|
|
||||||
// Check if mod is recommended by any others, only if not installed
|
// Check if mod is recommended by any others, only if not installed
|
||||||
if (!Loader::get()->isModInstalled(metadata.id)) {
|
if (!Loader::get()->isModInstalled(metadata.id)) {
|
||||||
|
@ -289,7 +293,7 @@ bool ModItem::init(ModSource&& source) {
|
||||||
->setDefaultScaleLimits(.1f, 1.f)
|
->setDefaultScaleLimits(.1f, 1.f)
|
||||||
->setAxisAlignment(AxisAlignment::Start)
|
->setAxisAlignment(AxisAlignment::Start)
|
||||||
);
|
);
|
||||||
m_infoContainer->addChild(m_recommendedBy);
|
m_infoContainer->addChildAtPosition(m_recommendedBy, Anchor::Left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,15 +333,89 @@ bool ModItem::init(ModSource&& source) {
|
||||||
|
|
||||||
void ModItem::updateState() {
|
void ModItem::updateState() {
|
||||||
auto wantsRestart = m_source.wantsRestart();
|
auto wantsRestart = m_source.wantsRestart();
|
||||||
|
|
||||||
auto download = server::ModDownloadManager::get()->getDownload(m_source.getID());
|
auto download = server::ModDownloadManager::get()->getDownload(m_source.getID());
|
||||||
bool isDownloading = download && download->isActive();
|
bool isDownloading = download && download->isActive();
|
||||||
|
|
||||||
|
// Update the size of the mod cell itself
|
||||||
|
if (m_display == ModListDisplay::Grid) {
|
||||||
|
auto widthWithoutGaps = m_targetWidth - 10;
|
||||||
|
this->setContentSize(ccp(widthWithoutGaps / roundf(widthWithoutGaps / 80), 100));
|
||||||
|
m_bg->setContentSize(m_obContentSize / m_bg->getScale());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->setContentSize(ccp(m_targetWidth, m_display == ModListDisplay::BigList ? 40 : 30));
|
||||||
|
m_bg->setContentSize((m_obContentSize - ccp(6, 0)) / m_bg->getScale());
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Grid layout the title is a direct child of info so it can be positioned
|
||||||
|
// more cleanly, while m_titleContainer is just used to position the version
|
||||||
|
// and downloads next to each other
|
||||||
|
m_titleLabel->removeFromParent();
|
||||||
|
if (m_display == ModListDisplay::Grid) {
|
||||||
|
m_infoContainer->addChildAtPosition(m_titleLabel, Anchor::Top);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_titleContainer->insertBefore(m_titleLabel, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_viewMenu->addChild(m_downloadCountContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move badges to either be next to the title or in the top left corner in grid view
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_badgeContainer->setLayout(RowLayout::create()->setAutoGrowAxis(true));
|
||||||
|
m_titleContainer->addChild(m_badgeContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Grid View logo has constant size
|
||||||
|
if (m_display == ModListDisplay::Grid) {
|
||||||
|
limitNodeSize(m_logo, ccp(30, 30), 999, .1f);
|
||||||
|
m_logo->setPosition(m_obContentSize.width / 2, m_obContentSize.height - 20);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto logoSize = m_obContentSize.height - 10;
|
||||||
|
limitNodeSize(m_logo, ccp(logoSize, logoSize), 999, .1f);
|
||||||
|
m_logo->setPosition(m_obContentSize.height / 2 + 5, m_obContentSize.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's space to show the description only on the big list
|
||||||
|
// When we do, elements like the download progress bar should replace it
|
||||||
|
// over the developer name since it's less important
|
||||||
|
// Couldn't figure out a more concise name
|
||||||
|
m_description->setVisible(m_display == ModListDisplay::BigList);
|
||||||
|
m_developers->setVisible(true);
|
||||||
|
auto elementToReplaceWithOtherAbnormalElement =
|
||||||
|
m_display == ModListDisplay::BigList ? m_description : m_developers;
|
||||||
|
|
||||||
|
auto titleSpace = m_display == ModListDisplay::Grid ?
|
||||||
|
CCSize(m_obContentSize.width - 10, 35) :
|
||||||
|
CCSize(m_obContentSize.width / 2 - m_obContentSize.height, m_obContentSize.height - 5);
|
||||||
|
|
||||||
|
// Divide by scale of info container since that actually determines the size
|
||||||
|
// (Since the scale of m_titleContainer and m_developers is managed by its layout)
|
||||||
|
|
||||||
// If there is an active download ongoing, show that in place of developer name
|
// If there is an active download ongoing, show that in place of developer name
|
||||||
|
// (or description on big view)
|
||||||
if (isDownloading) {
|
if (isDownloading) {
|
||||||
m_updateBtn->setVisible(false);
|
m_updateBtn->setVisible(false);
|
||||||
m_restartRequiredLabel->setVisible(false);
|
m_restartRequiredLabel->setVisible(false);
|
||||||
m_developers->setVisible(false);
|
elementToReplaceWithOtherAbnormalElement->setVisible(false);
|
||||||
|
|
||||||
auto status = download->getStatus();
|
auto status = download->getStatus();
|
||||||
if (auto prog = std::get_if<server::DownloadStatusDownloading>(&status)) {
|
if (auto prog = std::get_if<server::DownloadStatusDownloading>(&status)) {
|
||||||
|
@ -355,7 +433,7 @@ void ModItem::updateState() {
|
||||||
// Otherwise show "Restart Required" button if needed in place of dev name
|
// Otherwise show "Restart Required" button if needed in place of dev name
|
||||||
else {
|
else {
|
||||||
m_restartRequiredLabel->setVisible(wantsRestart);
|
m_restartRequiredLabel->setVisible(wantsRestart);
|
||||||
m_developers->setVisible(!wantsRestart);
|
elementToReplaceWithOtherAbnormalElement->setVisible(!wantsRestart);
|
||||||
m_downloadBarContainer->setVisible(false);
|
m_downloadBarContainer->setVisible(false);
|
||||||
m_downloadWaiting->setVisible(false);
|
m_downloadWaiting->setVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -418,11 +496,10 @@ void ModItem::updateState() {
|
||||||
m_versionLabel->setColor(to3B(ColorProvider::get()->color("mod-list-version-label"_spr)));
|
m_versionLabel->setColor(to3B(ColorProvider::get()->color("mod-list-version-label"_spr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_viewMenu->updateLayout();
|
// Hide by default
|
||||||
m_titleContainer->updateLayout();
|
m_outdatedLabel->setVisible(false);
|
||||||
|
|
||||||
// If there were problems, tint the BG red
|
// If there were problems, tint the BG red
|
||||||
m_outdatedLabel->setVisible(false);
|
|
||||||
if (m_source.asMod()) {
|
if (m_source.asMod()) {
|
||||||
std::optional<LoadProblem> targetsOutdated = m_source.asMod()->targetsOutdatedVersion();
|
std::optional<LoadProblem> targetsOutdated = m_source.asMod()->targetsOutdatedVersion();
|
||||||
if (m_source.asMod()->hasLoadProblems()) {
|
if (m_source.asMod()->hasLoadProblems()) {
|
||||||
|
@ -430,32 +507,129 @@ void ModItem::updateState() {
|
||||||
m_bg->setOpacity(isGeodeTheme() ? 25 : 90);
|
m_bg->setOpacity(isGeodeTheme() ? 25 : 90);
|
||||||
}
|
}
|
||||||
if (!wantsRestart && targetsOutdated && !isDownloading) {
|
if (!wantsRestart && targetsOutdated && !isDownloading) {
|
||||||
LoadProblem problem = targetsOutdated.value();
|
|
||||||
m_bg->setColor("mod-list-outdated-label"_cc3b);
|
m_bg->setColor("mod-list-outdated-label"_cc3b);
|
||||||
m_bg->setOpacity(isGeodeTheme() ? 25 : 90);
|
m_bg->setOpacity(isGeodeTheme() ? 25 : 90);
|
||||||
std::string content;
|
|
||||||
if (
|
|
||||||
problem.type == LoadProblem::Type::UnsupportedGeodeVersion ||
|
|
||||||
problem.type == LoadProblem::Type::NeedsNewerGeodeVersion
|
|
||||||
) {
|
|
||||||
content = fmt::format(
|
|
||||||
"Outdated (Geode {})",
|
|
||||||
m_source.getMetadata().getGeodeVersion().toNonVString()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
content = fmt::format(
|
|
||||||
"Outdated (GD {})",
|
|
||||||
m_source.getMetadata().getGameVersion().value_or("*")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
m_outdatedLabel->setString(content.c_str());
|
|
||||||
m_outdatedLabel->setVisible(true);
|
m_outdatedLabel->setVisible(true);
|
||||||
m_developers->setVisible(false);
|
elementToReplaceWithOtherAbnormalElement->setVisible(false);
|
||||||
|
if (m_display == ModListDisplay::Grid) {
|
||||||
|
m_outdatedLabel->setString("Outdated");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_outdatedLabel->setString((
|
||||||
|
(targetsOutdated->type == LoadProblem::Type::UnsupportedGeodeVersion ||
|
||||||
|
targetsOutdated->type == LoadProblem::Type::NeedsNewerGeodeVersion) ?
|
||||||
|
fmt::format(
|
||||||
|
"Outdated (Geode {})",
|
||||||
|
m_source.getMetadata().getGeodeVersion().toNonVString()
|
||||||
|
) :
|
||||||
|
fmt::format(
|
||||||
|
"Outdated (GD {})",
|
||||||
|
m_source.getMetadata().getGameVersion().value_or("*")
|
||||||
|
)
|
||||||
|
).c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update size and direction of title
|
||||||
|
// On grid view, m_titleContainer contains the version and download count
|
||||||
|
// but not the actual title lol
|
||||||
|
m_titleContainer->setContentWidth(titleSpace.width / m_infoContainer->getScale());
|
||||||
|
if (m_display == ModListDisplay::Grid) {
|
||||||
|
static_cast<RowLayout*>(m_titleContainer->getLayout())
|
||||||
|
->setGap(10)
|
||||||
|
->setAxisAlignment(AxisAlignment::Center);
|
||||||
|
static_cast<RowLayout*>(m_developers->getLayout())
|
||||||
|
->setAxisAlignment(AxisAlignment::Center);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
static_cast<RowLayout*>(m_titleContainer->getLayout())
|
||||||
|
->setGap(5)
|
||||||
|
->setAxisAlignment(AxisAlignment::Start);
|
||||||
|
static_cast<RowLayout*>(m_developers->getLayout())
|
||||||
|
->setAxisAlignment(AxisAlignment::Start);
|
||||||
|
}
|
||||||
|
m_titleContainer->updateLayout();
|
||||||
|
m_developers->setContentWidth(titleSpace.width / m_infoContainer->getScale());
|
||||||
|
m_developers->updateLayout();
|
||||||
|
|
||||||
|
if (m_recommendedBy) {
|
||||||
|
m_recommendedBy->setContentWidth(titleSpace.width / m_infoContainer->getScale());
|
||||||
|
m_recommendedBy->updateLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update positioning (jesus)
|
||||||
|
switch (m_display) {
|
||||||
|
case ModListDisplay::Grid: {
|
||||||
|
m_infoContainer->updateAnchoredPosition(Anchor::Center, ccp(0, -5), ccp(.5f, .5f));
|
||||||
|
// m_description is hidden
|
||||||
|
|
||||||
|
m_titleLabel->updateAnchoredPosition(Anchor::Top, ccp(0, -10), ccp(.5f, .5f));
|
||||||
|
limitNodeWidth(m_titleLabel, m_titleContainer->getContentWidth(), .8f, .1f);
|
||||||
|
m_titleContainer->updateAnchoredPosition(Anchor::Center, ccp(0, 0), ccp(.5f, .5f));
|
||||||
|
m_developers->updateAnchoredPosition(Anchor::Bottom, ccp(0, 10), ccp(.5f, .5f));
|
||||||
|
m_restartRequiredLabel->updateAnchoredPosition(Anchor::Bottom, ccp(0, 10), ccp(.5f, .5f));
|
||||||
|
m_outdatedLabel->updateAnchoredPosition(Anchor::Bottom, ccp(0, 10), ccp(.5f, .5f));
|
||||||
|
m_downloadBarContainer->updateAnchoredPosition(Anchor::Bottom, ccp(0, 10), ccp(.5f, .5f));
|
||||||
|
m_downloadWaiting->updateAnchoredPosition(Anchor::Bottom, ccp(0, 10), ccp(.5f, .5f));
|
||||||
|
|
||||||
|
if (m_recommendedBy) {
|
||||||
|
m_recommendedBy->updateAnchoredPosition(Anchor::Bottom, ccp(0, 10), ccp(.5f, .5f));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case ModListDisplay::SmallList: {
|
||||||
|
m_infoContainer->updateAnchoredPosition(Anchor::Left, ccp(m_obContentSize.height + 10, 0), ccp(0, .5f));
|
||||||
|
m_titleContainer->updateAnchoredPosition(Anchor::TopLeft, ccp(0, 0), ccp(0, 1));
|
||||||
|
|
||||||
|
// m_description is hidden
|
||||||
|
m_developers->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 3), ccp(0, 0));
|
||||||
|
m_restartRequiredLabel->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 3), ccp(0, 0));
|
||||||
|
m_outdatedLabel->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 3), ccp(0, 0));
|
||||||
|
m_downloadBarContainer->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 3), ccp(0, 0));
|
||||||
|
m_downloadWaiting->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 3), ccp(0, 0));
|
||||||
|
|
||||||
|
if (m_recommendedBy) {
|
||||||
|
m_recommendedBy->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 3), ccp(0, 0));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ModListDisplay::BigList: {
|
||||||
|
m_infoContainer->updateAnchoredPosition(Anchor::Left, ccp(m_obContentSize.height + 10, 0), ccp(0, .5f));
|
||||||
|
m_titleContainer->updateAnchoredPosition(Anchor::TopLeft, ccp(0, 0), ccp(0, 1));
|
||||||
|
|
||||||
|
m_developers->updateAnchoredPosition(Anchor::Left, ccp(0, 0), ccp(0, .5f));
|
||||||
|
|
||||||
|
m_description->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
|
||||||
|
m_restartRequiredLabel->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
|
||||||
|
m_outdatedLabel->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
|
||||||
|
m_downloadBarContainer->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
|
||||||
|
m_downloadWaiting->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
|
||||||
|
|
||||||
|
if (m_recommendedBy) {
|
||||||
|
m_recommendedBy->updateAnchoredPosition(Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
m_infoContainer->setContentSize(ccp(titleSpace.width, titleSpace.height) / m_infoContainer->getScale());
|
||||||
m_infoContainer->updateLayout();
|
m_infoContainer->updateLayout();
|
||||||
|
|
||||||
|
// Update button menu state
|
||||||
|
if (m_display == ModListDisplay::Grid) {
|
||||||
|
m_viewMenu->setContentWidth(m_obContentSize.width / m_viewMenu->getScaleX());
|
||||||
|
m_viewMenu->updateAnchoredPosition(Anchor::Bottom, ccp(0, 5), ccp(.5f, 0));
|
||||||
|
m_viewMenu->setScale(.45f);
|
||||||
|
static_cast<RowLayout*>(m_viewMenu->getLayout())->setAxisAlignment(AxisAlignment::Center);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_viewMenu->setContentWidth(m_obContentSize.width / m_viewMenu->getScaleX() / 2 - 20);
|
||||||
|
m_viewMenu->updateAnchoredPosition(Anchor::Right, ccp(-10, 0), ccp(1, .5f));
|
||||||
|
m_viewMenu->setScale(.55f);
|
||||||
|
static_cast<RowLayout*>(m_viewMenu->getLayout())->setAxisAlignment(AxisAlignment::End);
|
||||||
|
}
|
||||||
|
m_viewMenu->updateLayout();
|
||||||
|
|
||||||
// Highlight item via BG if it wants to restart for extra UI attention
|
// Highlight item via BG if it wants to restart for extra UI attention
|
||||||
if (wantsRestart) {
|
if (wantsRestart) {
|
||||||
m_bg->setColor("mod-list-restart-required-label"_cc3b);
|
m_bg->setColor("mod-list-restart-required-label"_cc3b);
|
||||||
|
@ -482,45 +656,15 @@ void ModItem::updateState() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->updateLayout();
|
||||||
|
|
||||||
ModItemUIEvent(std::make_unique<ModItemUIEvent::Impl>(this)).post();
|
ModItemUIEvent(std::make_unique<ModItemUIEvent::Impl>(this)).post();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModItem::updateSize(float width, bool big) {
|
void ModItem::updateDisplay(float width, ModListDisplay display) {
|
||||||
this->setContentSize({ width, big ? 40.f : 30.f });
|
m_display = display;
|
||||||
|
m_targetWidth = width;
|
||||||
m_bg->setContentSize((m_obContentSize - ccp(6, 0)) / m_bg->getScale());
|
this->updateState();
|
||||||
m_bg->setPosition(m_obContentSize / 2);
|
|
||||||
|
|
||||||
auto logoSize = m_obContentSize.height - 10;
|
|
||||||
limitNodeSize(m_logo, { logoSize, logoSize }, 999, .1f);
|
|
||||||
m_logo->setPosition(m_obContentSize.height / 2 + 5, m_obContentSize.height / 2);
|
|
||||||
|
|
||||||
CCSize titleSpace {
|
|
||||||
m_obContentSize.width / 2 - m_obContentSize.height,
|
|
||||||
logoSize + 5
|
|
||||||
};
|
|
||||||
|
|
||||||
// Divide by scale of info container since that actually determines the size
|
|
||||||
// (Since the scale of m_titleContainer and m_developers is managed by its layout)
|
|
||||||
m_titleContainer->setContentWidth(titleSpace.width / m_infoContainer->getScale());
|
|
||||||
m_titleContainer->updateLayout();
|
|
||||||
|
|
||||||
m_developers->setContentWidth(titleSpace.width / m_infoContainer->getScale());
|
|
||||||
m_developers->updateLayout();
|
|
||||||
|
|
||||||
if (m_recommendedBy) {
|
|
||||||
m_recommendedBy->setContentWidth(titleSpace.width / m_infoContainer->getScale());
|
|
||||||
m_recommendedBy->updateLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_infoContainer->setPosition(m_obContentSize.height + 10, m_obContentSize.height / 2);
|
|
||||||
m_infoContainer->setContentSize(ccp(titleSpace.width, titleSpace.height) / m_infoContainer->getScale());
|
|
||||||
m_infoContainer->updateLayout();
|
|
||||||
|
|
||||||
m_viewMenu->setContentWidth(m_obContentSize.width / m_viewMenu->getScaleX() / 2 - 20);
|
|
||||||
m_viewMenu->updateLayout();
|
|
||||||
|
|
||||||
this->updateLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModItem::onCheckUpdates(typename server::ServerRequest<std::optional<server::ServerModUpdate>>::Event* event) {
|
void ModItem::onCheckUpdates(typename server::ServerRequest<std::optional<server::ServerModUpdate>>::Event* event) {
|
||||||
|
|
|
@ -12,6 +12,12 @@
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
enum class ModListDisplay {
|
||||||
|
SmallList,
|
||||||
|
BigList,
|
||||||
|
Grid,
|
||||||
|
};
|
||||||
|
|
||||||
class ModItem : public CCNode {
|
class ModItem : public CCNode {
|
||||||
protected:
|
protected:
|
||||||
ModSource m_source;
|
ModSource m_source;
|
||||||
|
@ -19,10 +25,11 @@ protected:
|
||||||
CCNode* m_logo;
|
CCNode* m_logo;
|
||||||
CCNode* m_infoContainer;
|
CCNode* m_infoContainer;
|
||||||
CCNode* m_titleContainer;
|
CCNode* m_titleContainer;
|
||||||
CCLabelBMFont* m_titleLabel;
|
Ref<CCLabelBMFont> m_titleLabel;
|
||||||
CCLabelBMFont* m_versionLabel;
|
CCLabelBMFont* m_versionLabel;
|
||||||
CCNode* m_developers;
|
CCNode* m_developers;
|
||||||
CCNode* m_recommendedBy;
|
CCNode* m_recommendedBy;
|
||||||
|
CCScale9Sprite* m_description;
|
||||||
CCLabelBMFont* m_developerLabel;
|
CCLabelBMFont* m_developerLabel;
|
||||||
ButtonSprite* m_restartRequiredLabel;
|
ButtonSprite* m_restartRequiredLabel;
|
||||||
ButtonSprite* m_outdatedLabel;
|
ButtonSprite* m_outdatedLabel;
|
||||||
|
@ -37,6 +44,10 @@ protected:
|
||||||
EventListener<server::ModDownloadFilter> m_downloadListener;
|
EventListener<server::ModDownloadFilter> m_downloadListener;
|
||||||
std::optional<server::ServerModUpdate> m_availableUpdate;
|
std::optional<server::ServerModUpdate> m_availableUpdate;
|
||||||
EventListener<EventFilter<SettingNodeValueChangeEvent>> m_settingNodeListener;
|
EventListener<EventFilter<SettingNodeValueChangeEvent>> m_settingNodeListener;
|
||||||
|
Ref<CCNode> m_badgeContainer = nullptr;
|
||||||
|
Ref<CCNode> m_downloadCountContainer;
|
||||||
|
ModListDisplay m_display = ModListDisplay::SmallList;
|
||||||
|
float m_targetWidth = 300;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @warning Make sure `getMetadata` and `createModLogo` are callable
|
* @warning Make sure `getMetadata` and `createModLogo` are callable
|
||||||
|
@ -57,7 +68,7 @@ protected:
|
||||||
public:
|
public:
|
||||||
static ModItem* create(ModSource&& source);
|
static ModItem* create(ModSource&& source);
|
||||||
|
|
||||||
void updateSize(float width, bool big);
|
void updateDisplay(float width, ModListDisplay display);
|
||||||
|
|
||||||
ModSource& getSource() &;
|
ModSource& getSource() &;
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,13 +18,6 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
|
||||||
m_source->reset();
|
m_source->reset();
|
||||||
|
|
||||||
m_list = ScrollLayer::create(size);
|
m_list = ScrollLayer::create(size);
|
||||||
m_list->m_contentLayer->setLayout(
|
|
||||||
ColumnLayout::create()
|
|
||||||
->setAxisReverse(true)
|
|
||||||
->setAxisAlignment(AxisAlignment::End)
|
|
||||||
->setAutoGrowAxis(size.height)
|
|
||||||
->setGap(2.5f)
|
|
||||||
);
|
|
||||||
this->addChildAtPosition(m_list, Anchor::Bottom, ccp(-m_list->getScaledContentWidth() / 2, 0));
|
this->addChildAtPosition(m_list, Anchor::Bottom, ccp(-m_list->getScaledContentWidth() / 2, 0));
|
||||||
|
|
||||||
m_topContainer = CCNode::create();
|
m_topContainer = CCNode::create();
|
||||||
|
@ -410,7 +403,7 @@ void ModList::onPromise(ModListSource::PageLoadTask::Event* event) {
|
||||||
first = false;
|
first = false;
|
||||||
m_list->m_contentLayer->addChild(item);
|
m_list->m_contentLayer->addChild(item);
|
||||||
}
|
}
|
||||||
this->updateSize(m_bigSize);
|
this->updateDisplay(m_display);
|
||||||
|
|
||||||
// Scroll list to top
|
// Scroll list to top
|
||||||
auto listTopScrollPos = -m_list->m_contentLayer->getContentHeight() + m_list->getContentHeight();
|
auto listTopScrollPos = -m_list->m_contentLayer->getContentHeight() + m_list->getContentHeight();
|
||||||
|
@ -526,12 +519,10 @@ void ModList::updateTopContainer() {
|
||||||
// (giving a little bit of extra padding for it, the same size as gap)
|
// (giving a little bit of extra padding for it, the same size as gap)
|
||||||
m_list->setContentHeight(
|
m_list->setContentHeight(
|
||||||
m_topContainer->getContentHeight() > 0.f ?
|
m_topContainer->getContentHeight() > 0.f ?
|
||||||
this->getContentHeight() - m_topContainer->getContentHeight() -
|
this->getContentHeight() - m_topContainer->getContentHeight() - 2.5f :
|
||||||
static_cast<AxisLayout*>(m_list->m_contentLayer->getLayout())->getGap() :
|
|
||||||
this->getContentHeight()
|
this->getContentHeight()
|
||||||
);
|
);
|
||||||
static_cast<ColumnLayout*>(m_list->m_contentLayer->getLayout())->setAutoGrowAxis(m_list->getContentHeight());
|
this->updateDisplay(m_display);
|
||||||
m_list->m_contentLayer->updateLayout();
|
|
||||||
|
|
||||||
// Preserve relative scroll position
|
// Preserve relative scroll position
|
||||||
m_list->m_contentLayer->setPositionY((
|
m_list->m_contentLayer->setPositionY((
|
||||||
|
@ -557,14 +548,14 @@ void ModList::updateTopContainer() {
|
||||||
this->updateLayout();
|
this->updateLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModList::updateSize(bool big) {
|
void ModList::updateDisplay(ModListDisplay display) {
|
||||||
m_bigSize = big;
|
m_display = display;
|
||||||
|
|
||||||
// Update all BaseModItems that are children of the list
|
// Update all BaseModItems that are children of the list
|
||||||
// There may be non-BaseModItems there (like separators) so gotta be type-safe
|
// There may be non-BaseModItems there (like separators) so gotta be type-safe
|
||||||
for (auto& node : CCArrayExt<CCNode*>(m_list->m_contentLayer->getChildren())) {
|
for (auto& node : CCArrayExt<CCNode*>(m_list->m_contentLayer->getChildren())) {
|
||||||
if (auto item = typeinfo_cast<ModItem*>(node)) {
|
if (auto item = typeinfo_cast<ModItem*>(node)) {
|
||||||
item->updateSize(m_list->getContentWidth(), big);
|
item->updateDisplay(m_list->getContentWidth(), display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,8 +565,24 @@ void ModList::updateSize(bool big) {
|
||||||
m_list->m_contentLayer->getPositionY() / oldPositionArea :
|
m_list->m_contentLayer->getPositionY() / oldPositionArea :
|
||||||
-1.f;
|
-1.f;
|
||||||
|
|
||||||
// Auto-grow the size of the list content
|
// Update the list layout based on the display model
|
||||||
m_list->m_contentLayer->updateLayout();
|
if (display == ModListDisplay::Grid) {
|
||||||
|
m_list->m_contentLayer->setLayout(
|
||||||
|
RowLayout::create()
|
||||||
|
->setGrowCrossAxis(true)
|
||||||
|
->setAxisAlignment(AxisAlignment::Center)
|
||||||
|
->setGap(2.5f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_list->m_contentLayer->setLayout(
|
||||||
|
ColumnLayout::create()
|
||||||
|
->setAxisReverse(true)
|
||||||
|
->setAxisAlignment(AxisAlignment::End)
|
||||||
|
->setAutoGrowAxis(m_obContentSize.height)
|
||||||
|
->setGap(2.5f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Preserve relative scroll position
|
// Preserve relative scroll position
|
||||||
m_list->m_contentLayer->setPositionY((
|
m_list->m_contentLayer->setPositionY((
|
||||||
|
|
|
@ -52,7 +52,7 @@ protected:
|
||||||
EventListener<InvalidateCacheFilter> m_invalidateCacheListener;
|
EventListener<InvalidateCacheFilter> m_invalidateCacheListener;
|
||||||
EventListener<server::ServerRequest<std::vector<std::string>>> m_checkUpdatesListener;
|
EventListener<server::ServerRequest<std::vector<std::string>>> m_checkUpdatesListener;
|
||||||
EventListener<server::ModDownloadFilter> m_downloadListener;
|
EventListener<server::ModDownloadFilter> m_downloadListener;
|
||||||
bool m_bigSize = false;
|
ModListDisplay m_display = ModListDisplay::SmallList;
|
||||||
bool m_exiting = false;
|
bool m_exiting = false;
|
||||||
std::atomic<size_t> m_searchInputThreads = 0;
|
std::atomic<size_t> m_searchInputThreads = 0;
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ public:
|
||||||
void showStatus(ModListStatus status, std::string const& message, std::optional<std::string> const& details = std::nullopt);
|
void showStatus(ModListStatus status, std::string const& message, std::optional<std::string> const& details = std::nullopt);
|
||||||
|
|
||||||
void updateState();
|
void updateState();
|
||||||
void updateSize(bool big);
|
void updateDisplay(ModListDisplay display);
|
||||||
void activateSearch(bool activate);
|
void activateSearch(bool activate);
|
||||||
void setIsExiting(bool exiting);
|
void setIsExiting(bool exiting);
|
||||||
};
|
};
|
||||||
|
|
|
@ -59,6 +59,8 @@ std::string utils::string::toUpper(std::string const& str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string& utils::string::replaceIP(std::string& str, std::string const& orig, std::string const& repl) {
|
std::string& utils::string::replaceIP(std::string& str, std::string const& orig, std::string const& repl) {
|
||||||
|
if (orig.empty()) return str;
|
||||||
|
|
||||||
std::string::size_type n = 0;
|
std::string::size_type n = 0;
|
||||||
while ((n = str.find(orig, n)) != std::string::npos) {
|
while ((n = str.find(orig, n)) != std::string::npos) {
|
||||||
str.replace(n, orig.size(), repl);
|
str.replace(n, orig.size(), repl);
|
||||||
|
|
Loading…
Reference in a new issue