mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-30 03:15:38 -05:00
Compare commits
6 commits
468dea6346
...
c06c5aff25
Author | SHA1 | Date | |
---|---|---|---|
|
c06c5aff25 | ||
|
7bcf50da57 | ||
|
38f3385c90 | ||
|
4d5e465ade | ||
|
bd6bfb661f | ||
|
03e8220dfe |
13 changed files with 475 additions and 143 deletions
41
loader/include/Geode/ui/OverlayManager.hpp
Normal file
41
loader/include/Geode/ui/OverlayManager.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
|
||||
#include <cocos2d.h>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
|
||||
namespace geode
|
||||
{
|
||||
/*
|
||||
Because cocos only allows for one notification node (a node drawn last, above the fps counter and everything),
|
||||
I added this, a simple class to add nodes to a general notification node so that mods dont interfere with each other.
|
||||
*/
|
||||
|
||||
class GEODE_DLL OverlayManager : private cocos2d::CCNode
|
||||
{
|
||||
private:
|
||||
std::vector<cocos2d::CCNode*> nodes;
|
||||
|
||||
public:
|
||||
/// @brief Get the overlay manager instance, and if it doesnt exist, sets the notification node to it
|
||||
static OverlayManager* get();
|
||||
|
||||
/// @brief Adds a node to the overlay manager, overlays are sorted by ZOrder, the higher the order is the later it draws. This will retain the node
|
||||
void addNode(cocos2d::CCNode* node);
|
||||
|
||||
/// @brief Removes a node from the overlay manager, stopping it from being drawn. This will release the node
|
||||
void removeNode(cocos2d::CCNode* node);
|
||||
|
||||
/// @brief Util to get the highest ZOrder of all nodes
|
||||
int getHighestOverlayZOrder();
|
||||
|
||||
/// @brief Util to get the lowest ZOrder of all nodes
|
||||
int getLowestOverlayZOrder();
|
||||
|
||||
/// @brief Gets all the overlays
|
||||
std::vector<cocos2d::CCNode*> getOverlays();
|
||||
};
|
||||
};
|
|
@ -71,6 +71,7 @@ namespace geode::utils {
|
|||
std::string m_msg;
|
||||
Timer<Clock> m_timer;
|
||||
|
||||
// @geode-ignore(geode-alternative)
|
||||
LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) :
|
||||
m_msg(msg), m_output(out) {
|
||||
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>
|
||||
|
||||
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
|
||||
// @geode-ignore(geode-alternative)
|
||||
std::cout << title << ": " << info << std::endl;
|
||||
}
|
||||
|
||||
void Loader::Impl::logConsoleMessageWithSeverity(std::string const& msg, Severity severity) {
|
||||
if (m_platformConsoleOpen) {
|
||||
// @geode-ignore(geode-alternative)
|
||||
std::cout << msg << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ enum {
|
|||
ID_BUTTON_OPEN_FOLDER = 103,
|
||||
ID_BUTTON_COPY_CLIPBOARD = 104,
|
||||
ID_BUTTON_RESTART_GAME = 105,
|
||||
ID_SAFE_MODE_TIP_TEXT = 106,
|
||||
};
|
||||
#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"));
|
||||
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(
|
||||
"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,
|
||||
|
@ -109,10 +129,20 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||
RECT 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(
|
||||
GetDlgItem(hwnd, ID_CRASHLOG_TEXT), NULL,
|
||||
layout::PADDING, layout::PADDING,
|
||||
clientRect.right - layout::PADDING * 2, clientRect.bottom - layout::BUTTON_HEIGHT - layout::PADDING * 3,
|
||||
layout::PADDING, layout::PADDING * 2 + textRect.bottom,
|
||||
clientRect.right - layout::PADDING * 2, clientRect.bottom - layout::BUTTON_HEIGHT - layout::PADDING * 4 - textRect.bottom,
|
||||
SWP_NOZORDER
|
||||
);
|
||||
|
||||
|
@ -150,6 +180,9 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||
} break;
|
||||
|
||||
case WM_CTLCOLORSTATIC: {
|
||||
auto hdc = (HDC)wParam;
|
||||
// make every text have transparent background
|
||||
SetBkMode(hdc, TRANSPARENT);
|
||||
return (LRESULT)(COLOR_WINDOWFRAME);
|
||||
} break;
|
||||
|
||||
|
|
|
@ -499,28 +499,48 @@ bool ModsLayer::init() {
|
|||
|
||||
// Actions
|
||||
|
||||
auto listActionsMenu = CCMenu::create();
|
||||
listActionsMenu->setID("list-actions-menu");
|
||||
listActionsMenu->setContentHeight(100);
|
||||
listActionsMenu->setAnchorPoint({ 1, 0 });
|
||||
listActionsMenu->setScale(.65f);
|
||||
auto listDisplayMenu = CCMenu::create();
|
||||
listDisplayMenu->setID("list-actions-menu");
|
||||
listDisplayMenu->setContentHeight(100);
|
||||
listDisplayMenu->setAnchorPoint({ 1, 0 });
|
||||
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(
|
||||
GeodeSquareSprite::createWithSpriteFrameName("GJ_smallModeIcon_001.png", &m_bigView),
|
||||
this, menu_selector(ModsLayer::onBigView)
|
||||
GeodeSquareSprite::createWithSpriteFrameName("GJ_extendedIcon_001.png"),
|
||||
this, menu_selector(ModsLayer::onDisplay)
|
||||
);
|
||||
bigSizeBtn->setTag(static_cast<int>(ModListDisplay::BigList));
|
||||
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(
|
||||
// GeodeSquareSprite::createWithSpriteFrameName("search.png"_spr, &m_showSearch),
|
||||
// this, menu_selector(ModsLayer::onSearch)
|
||||
// );
|
||||
// searchBtn->setID("search-button");
|
||||
// listActionsMenu->addChild(searchBtn);
|
||||
// listDisplayMenu->addChild(searchBtn);
|
||||
|
||||
listActionsMenu->setLayout(ColumnLayout::create());
|
||||
m_frame->addChildAtPosition(listActionsMenu, Anchor::Left, ccp(-5, 25));
|
||||
listDisplayMenu->setLayout(ColumnLayout::create()->setAxisReverse(true));
|
||||
m_frame->addChildAtPosition(listDisplayMenu, Anchor::Left, ccp(-5, 25));
|
||||
|
||||
m_statusNode = ModsStatusNode::create();
|
||||
m_statusNode->setZOrder(4);
|
||||
|
@ -629,7 +649,7 @@ void ModsLayer::gotoTab(ModListSource* src) {
|
|||
m_currentSource = src;
|
||||
|
||||
// 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)->updateState();
|
||||
}
|
||||
|
@ -687,6 +707,13 @@ void ModsLayer::updateState() {
|
|||
else {
|
||||
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) {
|
||||
|
@ -716,12 +743,13 @@ void ModsLayer::onGoToPage(CCObject*) {
|
|||
popup->setID("go-to-page"_spr);
|
||||
popup->show();
|
||||
}
|
||||
void ModsLayer::onBigView(CCObject*) {
|
||||
m_bigView = !m_bigView;
|
||||
void ModsLayer::onDisplay(CCObject* sender) {
|
||||
m_display = static_cast<ModListDisplay>(sender->getTag());
|
||||
// Make sure to avoid a crash
|
||||
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*) {
|
||||
m_showSearch = !m_showSearch;
|
||||
|
|
|
@ -66,7 +66,8 @@ protected:
|
|||
ModsStatusNode* m_statusNode;
|
||||
EventListener<UpdateModListStateFilter> m_updateStateListener;
|
||||
bool m_showSearch = true;
|
||||
bool m_bigView = false;
|
||||
ModListDisplay m_display = ModListDisplay::SmallList;
|
||||
std::vector<CCMenuItemSpriteExtra*> m_displayBtns;
|
||||
|
||||
bool init();
|
||||
|
||||
|
@ -77,7 +78,7 @@ protected:
|
|||
void onTab(CCObject* sender);
|
||||
void onOpenModsFolder(CCObject*);
|
||||
void onAddModFromFile(CCObject*);
|
||||
void onBigView(CCObject*);
|
||||
void onDisplay(CCObject*);
|
||||
void onSearch(CCObject*);
|
||||
void onGoToPage(CCObject*);
|
||||
void onRefreshList(CCObject*);
|
||||
|
|
|
@ -29,7 +29,7 @@ bool ModItem::init(ModSource&& source) {
|
|||
m_bg->ignoreAnchorPointForPosition(false);
|
||||
m_bg->setAnchorPoint({ .5f, .5f });
|
||||
m_bg->setScale(.7f);
|
||||
this->addChild(m_bg);
|
||||
this->addChildAtPosition(m_bg, Anchor::Center);
|
||||
|
||||
m_logo = m_source.createModLogo();
|
||||
m_logo->setID("logo-sprite");
|
||||
|
@ -39,23 +39,10 @@ bool ModItem::init(ModSource&& source) {
|
|||
m_infoContainer->setID("info-container");
|
||||
m_infoContainer->setScale(.4f);
|
||||
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->setID("title-container");
|
||||
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->setID("title-label");
|
||||
|
@ -67,14 +54,20 @@ bool ModItem::init(ModSource&& source) {
|
|||
m_versionLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .7f));
|
||||
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->setID("developers-menu");
|
||||
m_developers->ignoreAnchorPointForPosition(false);
|
||||
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->setID("developers-label");
|
||||
auto developersBtn = CCMenuItemSpriteExtra::create(
|
||||
|
@ -87,7 +80,24 @@ bool ModItem::init(ModSource&& source) {
|
|||
RowLayout::create()
|
||||
->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(
|
||||
"Restart Required",
|
||||
|
@ -97,19 +107,19 @@ bool ModItem::init(ModSource&& source) {
|
|||
}
|
||||
);
|
||||
m_restartRequiredLabel->setID("restart-required-label");
|
||||
m_restartRequiredLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
|
||||
m_infoContainer->addChild(m_restartRequiredLabel);
|
||||
m_restartRequiredLabel->setScale(.75f);
|
||||
m_infoContainer->addChildAtPosition(m_restartRequiredLabel, Anchor::Left);
|
||||
|
||||
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-bg"_spr))
|
||||
}
|
||||
);
|
||||
m_outdatedLabel->setID("outdated-label");
|
||||
m_outdatedLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
|
||||
m_infoContainer->addChild(m_outdatedLabel);
|
||||
m_outdatedLabel->setScale(.75f);
|
||||
m_infoContainer->addChildAtPosition(m_outdatedLabel, Anchor::Left);
|
||||
|
||||
m_downloadBarContainer = CCNode::create();
|
||||
m_downloadBarContainer->setID("download-bar-container");
|
||||
|
@ -121,7 +131,7 @@ bool ModItem::init(ModSource&& source) {
|
|||
m_downloadBar->setScale(1.5f);
|
||||
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->setID("download-waiting-container");
|
||||
|
@ -141,13 +151,12 @@ bool ModItem::init(ModSource&& source) {
|
|||
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->setID("view-menu");
|
||||
m_viewMenu->setAnchorPoint({ 1.f, .5f });
|
||||
m_viewMenu->setScale(.55f);
|
||||
|
||||
ButtonSprite* spr = nullptr;
|
||||
|
@ -214,43 +223,38 @@ bool ModItem::init(ModSource&& source) {
|
|||
}
|
||||
},
|
||||
[this](server::ServerModMetadata const& metadata) {
|
||||
m_badgeContainer = CCNode::create();
|
||||
m_badgeContainer->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
||||
if (metadata.featured) {
|
||||
auto star = CCSprite::createWithSpriteFrameName("tag-featured.png"_spr);
|
||||
star->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
||||
m_titleContainer->addChild(star);
|
||||
m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-featured.png"_spr));
|
||||
}
|
||||
if (metadata.tags.contains("paid")) {
|
||||
auto paidModLabel = CCSprite::createWithSpriteFrameName("tag-paid.png"_spr);
|
||||
paidModLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
||||
m_titleContainer->addChild(paidModLabel);
|
||||
m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-paid.png"_spr));
|
||||
}
|
||||
if (metadata.tags.contains("modtober24")) {
|
||||
auto modtoberLabel = CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr);
|
||||
modtoberLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
||||
m_titleContainer->addChild(modtoberLabel);
|
||||
m_badgeContainer->addChild(CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr));
|
||||
}
|
||||
|
||||
// Show mod download count here already so people can make informed decisions
|
||||
// on which mods to install
|
||||
auto downloadsContainer = CCNode::create();
|
||||
m_downloadCountContainer = CCNode::create();
|
||||
|
||||
auto downloads = CCLabelBMFont::create(numToAbbreviatedString(metadata.downloadCount).c_str(), "bigFont.fnt");
|
||||
downloads->setID("downloads-label");
|
||||
downloads->setColor("mod-list-version-label"_cc3b);
|
||||
downloads->limitLabelWidth(80, .5f, .1f);
|
||||
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");
|
||||
downloadsIcon->setID("downloads-icon-sprite");
|
||||
downloadsIcon->setScale(.75f);
|
||||
downloadsContainer->addChildAtPosition(downloadsIcon, Anchor::Right, ccp(-downloads->getScaledContentWidth() - 10, 0));
|
||||
m_downloadCountContainer->addChildAtPosition(downloadsIcon, Anchor::Left, ccp(5, 0));
|
||||
|
||||
downloadsContainer->setContentSize({
|
||||
downloads->getScaledContentWidth() + 10 + downloadsIcon->getScaledContentWidth() + 10,
|
||||
m_downloadCountContainer->setContentSize({
|
||||
downloads->getScaledContentWidth() + downloadsIcon->getScaledContentWidth(),
|
||||
25
|
||||
});
|
||||
downloadsContainer->updateLayout();
|
||||
m_viewMenu->addChild(downloadsContainer);
|
||||
m_downloadCountContainer->updateLayout();
|
||||
|
||||
// Check if mod is recommended by any others, only if not installed
|
||||
if (!Loader::get()->isModInstalled(metadata.id)) {
|
||||
|
@ -289,7 +293,7 @@ bool ModItem::init(ModSource&& source) {
|
|||
->setDefaultScaleLimits(.1f, 1.f)
|
||||
->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() {
|
||||
auto wantsRestart = m_source.wantsRestart();
|
||||
|
||||
auto download = server::ModDownloadManager::get()->getDownload(m_source.getID());
|
||||
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
|
||||
// (or description on big view)
|
||||
if (isDownloading) {
|
||||
m_updateBtn->setVisible(false);
|
||||
m_restartRequiredLabel->setVisible(false);
|
||||
m_developers->setVisible(false);
|
||||
elementToReplaceWithOtherAbnormalElement->setVisible(false);
|
||||
|
||||
auto status = download->getStatus();
|
||||
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
|
||||
else {
|
||||
m_restartRequiredLabel->setVisible(wantsRestart);
|
||||
m_developers->setVisible(!wantsRestart);
|
||||
elementToReplaceWithOtherAbnormalElement->setVisible(!wantsRestart);
|
||||
m_downloadBarContainer->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_viewMenu->updateLayout();
|
||||
m_titleContainer->updateLayout();
|
||||
// Hide by default
|
||||
m_outdatedLabel->setVisible(false);
|
||||
|
||||
// If there were problems, tint the BG red
|
||||
m_outdatedLabel->setVisible(false);
|
||||
if (m_source.asMod()) {
|
||||
std::optional<LoadProblem> targetsOutdated = m_source.asMod()->targetsOutdatedVersion();
|
||||
if (m_source.asMod()->hasLoadProblems()) {
|
||||
|
@ -430,32 +507,129 @@ void ModItem::updateState() {
|
|||
m_bg->setOpacity(isGeodeTheme() ? 25 : 90);
|
||||
}
|
||||
if (!wantsRestart && targetsOutdated && !isDownloading) {
|
||||
LoadProblem problem = targetsOutdated.value();
|
||||
m_bg->setColor("mod-list-outdated-label"_cc3b);
|
||||
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_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();
|
||||
|
||||
// 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
|
||||
if (wantsRestart) {
|
||||
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();
|
||||
}
|
||||
|
||||
void ModItem::updateSize(float width, bool big) {
|
||||
this->setContentSize({ width, big ? 40.f : 30.f });
|
||||
|
||||
m_bg->setContentSize((m_obContentSize - ccp(6, 0)) / m_bg->getScale());
|
||||
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::updateDisplay(float width, ModListDisplay display) {
|
||||
m_display = display;
|
||||
m_targetWidth = width;
|
||||
this->updateState();
|
||||
}
|
||||
|
||||
void ModItem::onCheckUpdates(typename server::ServerRequest<std::optional<server::ServerModUpdate>>::Event* event) {
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
|
||||
using namespace geode::prelude;
|
||||
|
||||
enum class ModListDisplay {
|
||||
SmallList,
|
||||
BigList,
|
||||
Grid,
|
||||
};
|
||||
|
||||
class ModItem : public CCNode {
|
||||
protected:
|
||||
ModSource m_source;
|
||||
|
@ -19,10 +25,11 @@ protected:
|
|||
CCNode* m_logo;
|
||||
CCNode* m_infoContainer;
|
||||
CCNode* m_titleContainer;
|
||||
CCLabelBMFont* m_titleLabel;
|
||||
Ref<CCLabelBMFont> m_titleLabel;
|
||||
CCLabelBMFont* m_versionLabel;
|
||||
CCNode* m_developers;
|
||||
CCNode* m_recommendedBy;
|
||||
CCScale9Sprite* m_description;
|
||||
CCLabelBMFont* m_developerLabel;
|
||||
ButtonSprite* m_restartRequiredLabel;
|
||||
ButtonSprite* m_outdatedLabel;
|
||||
|
@ -37,6 +44,10 @@ protected:
|
|||
EventListener<server::ModDownloadFilter> m_downloadListener;
|
||||
std::optional<server::ServerModUpdate> m_availableUpdate;
|
||||
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
|
||||
|
@ -57,7 +68,7 @@ protected:
|
|||
public:
|
||||
static ModItem* create(ModSource&& source);
|
||||
|
||||
void updateSize(float width, bool big);
|
||||
void updateDisplay(float width, ModListDisplay display);
|
||||
|
||||
ModSource& getSource() &;
|
||||
};
|
||||
|
|
|
@ -18,13 +18,6 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
|
|||
m_source->reset();
|
||||
|
||||
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));
|
||||
|
||||
m_topContainer = CCNode::create();
|
||||
|
@ -410,7 +403,7 @@ void ModList::onPromise(ModListSource::PageLoadTask::Event* event) {
|
|||
first = false;
|
||||
m_list->m_contentLayer->addChild(item);
|
||||
}
|
||||
this->updateSize(m_bigSize);
|
||||
this->updateDisplay(m_display);
|
||||
|
||||
// Scroll list to top
|
||||
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)
|
||||
m_list->setContentHeight(
|
||||
m_topContainer->getContentHeight() > 0.f ?
|
||||
this->getContentHeight() - m_topContainer->getContentHeight() -
|
||||
static_cast<AxisLayout*>(m_list->m_contentLayer->getLayout())->getGap() :
|
||||
this->getContentHeight() - m_topContainer->getContentHeight() - 2.5f :
|
||||
this->getContentHeight()
|
||||
);
|
||||
static_cast<ColumnLayout*>(m_list->m_contentLayer->getLayout())->setAutoGrowAxis(m_list->getContentHeight());
|
||||
m_list->m_contentLayer->updateLayout();
|
||||
this->updateDisplay(m_display);
|
||||
|
||||
// Preserve relative scroll position
|
||||
m_list->m_contentLayer->setPositionY((
|
||||
|
@ -557,14 +548,14 @@ void ModList::updateTopContainer() {
|
|||
this->updateLayout();
|
||||
}
|
||||
|
||||
void ModList::updateSize(bool big) {
|
||||
m_bigSize = big;
|
||||
void ModList::updateDisplay(ModListDisplay display) {
|
||||
m_display = display;
|
||||
|
||||
// Update all BaseModItems that are children of the list
|
||||
// There may be non-BaseModItems there (like separators) so gotta be type-safe
|
||||
for (auto& node : CCArrayExt<CCNode*>(m_list->m_contentLayer->getChildren())) {
|
||||
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 :
|
||||
-1.f;
|
||||
|
||||
// Auto-grow the size of the list content
|
||||
m_list->m_contentLayer->updateLayout();
|
||||
// Update the list layout based on the display model
|
||||
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
|
||||
m_list->m_contentLayer->setPositionY((
|
||||
|
|
|
@ -52,7 +52,7 @@ protected:
|
|||
EventListener<InvalidateCacheFilter> m_invalidateCacheListener;
|
||||
EventListener<server::ServerRequest<std::vector<std::string>>> m_checkUpdatesListener;
|
||||
EventListener<server::ModDownloadFilter> m_downloadListener;
|
||||
bool m_bigSize = false;
|
||||
ModListDisplay m_display = ModListDisplay::SmallList;
|
||||
bool m_exiting = false;
|
||||
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 updateState();
|
||||
void updateSize(bool big);
|
||||
void updateDisplay(ModListDisplay display);
|
||||
void activateSearch(bool activate);
|
||||
void setIsExiting(bool exiting);
|
||||
};
|
||||
|
|
62
loader/src/ui/nodes/OverlayManager.cpp
Normal file
62
loader/src/ui/nodes/OverlayManager.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include <Geode/ui/OverlayManager.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
OverlayManager* OverlayManager::get()
|
||||
{
|
||||
static OverlayManager* instance;
|
||||
|
||||
if (!instance)
|
||||
{
|
||||
instance = new OverlayManager();
|
||||
|
||||
CCDirector::get()->setNotificationNode(instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void OverlayManager::addNode(CCNode* node)
|
||||
{
|
||||
this->addChild(node);
|
||||
|
||||
nodes.push_back(node);
|
||||
}
|
||||
|
||||
void OverlayManager::removeNode(CCNode* node)
|
||||
{
|
||||
this->removeChild(node);
|
||||
|
||||
std::erase(nodes, node);
|
||||
}
|
||||
|
||||
int OverlayManager::getHighestOverlayZOrder()
|
||||
{
|
||||
int z = INT_MIN;
|
||||
|
||||
for (auto node : nodes)
|
||||
{
|
||||
if (node->getZOrder() > z)
|
||||
z = node->getZOrder();
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
int OverlayManager::getLowestOverlayZOrder()
|
||||
{
|
||||
int z = INT_MAX;
|
||||
|
||||
for (auto node : nodes)
|
||||
{
|
||||
if (node->getZOrder() < z)
|
||||
z = node->getZOrder();
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
std::vector<CCNode*> OverlayManager::getOverlays()
|
||||
{
|
||||
return nodes;
|
||||
}
|
|
@ -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) {
|
||||
if (orig.empty()) return str;
|
||||
|
||||
std::string::size_type n = 0;
|
||||
while ((n = str.find(orig, n)) != std::string::npos) {
|
||||
str.replace(n, orig.size(), repl);
|
||||
|
|
Loading…
Reference in a new issue