mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-27 09:55:34 -05:00
new install list ui
This commit is contained in:
parent
0e1d639002
commit
3707418355
6 changed files with 716 additions and 55 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include <Geode/utils/ranges.hpp>
|
#include <Geode/utils/ranges.hpp>
|
||||||
#include <Geode/utils/web.hpp>
|
#include <Geode/utils/web.hpp>
|
||||||
#include <loader/LoaderImpl.hpp>
|
#include <loader/LoaderImpl.hpp>
|
||||||
|
#include <ui/internal/list/InstallListPopup.hpp>
|
||||||
|
|
||||||
static constexpr int const TAG_CONFIRM_UNINSTALL = 5;
|
static constexpr int const TAG_CONFIRM_UNINSTALL = 5;
|
||||||
static constexpr int const TAG_CONFIRM_UPDATE = 6;
|
static constexpr int const TAG_CONFIRM_UPDATE = 6;
|
||||||
|
@ -749,43 +750,7 @@ void IndexItemInfoPopup::onInstallProgress(ModInstallEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndexItemInfoPopup::onInstall(CCObject*) {
|
void IndexItemInfoPopup::onInstall(CCObject*) {
|
||||||
auto list = Index::get()->getInstallList(m_item);
|
InstallListPopup::create(m_item, [&](IndexInstallList const& list) {
|
||||||
if (!list) {
|
|
||||||
return FLAlertLayer::create(
|
|
||||||
"Unable to Install",
|
|
||||||
list.unwrapErr(),
|
|
||||||
"OK"
|
|
||||||
)->show();
|
|
||||||
}
|
|
||||||
FLAlertLayer::create(
|
|
||||||
this,
|
|
||||||
"Confirm Install",
|
|
||||||
fmt::format(
|
|
||||||
"The following mods will be installed:\n {}",
|
|
||||||
// le nest
|
|
||||||
ranges::join(
|
|
||||||
ranges::map<std::vector<std::string>>(
|
|
||||||
list.unwrap().list,
|
|
||||||
[](IndexItemHandle handle) {
|
|
||||||
return fmt::format(
|
|
||||||
" - <cr>{}</c> (<cy>{}</c>)",
|
|
||||||
handle->getMetadata().getName(),
|
|
||||||
handle->getMetadata().getID()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"\n "
|
|
||||||
)
|
|
||||||
),
|
|
||||||
"Cancel", "OK"
|
|
||||||
)->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexItemInfoPopup::onCancel(CCObject*) {
|
|
||||||
Index::get()->cancelInstall(m_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexItemInfoPopup::doInstall() {
|
|
||||||
if (m_latestVersionLabel) {
|
if (m_latestVersionLabel) {
|
||||||
m_latestVersionLabel->setVisible(false);
|
m_latestVersionLabel->setVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -797,13 +762,12 @@ void IndexItemInfoPopup::doInstall() {
|
||||||
m_installBtnSpr->setString("Cancel");
|
m_installBtnSpr->setString("Cancel");
|
||||||
m_installBtnSpr->setBG("GJ_button_06.png", false);
|
m_installBtnSpr->setBG("GJ_button_06.png", false);
|
||||||
|
|
||||||
Index::get()->install(m_item);
|
Index::get()->install(list);
|
||||||
|
})->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndexItemInfoPopup::FLAlert_Clicked(FLAlertLayer*, bool btn2) {
|
void IndexItemInfoPopup::onCancel(CCObject*) {
|
||||||
if (btn2) {
|
Index::get()->cancelInstall(m_item);
|
||||||
this->doInstall();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CCNode* IndexItemInfoPopup::createLogo(CCSize const& size) {
|
CCNode* IndexItemInfoPopup::createLogo(CCSize const& size) {
|
||||||
|
|
|
@ -89,7 +89,7 @@ public:
|
||||||
static LocalModInfoPopup* create(Mod* mod, ModListLayer* list);
|
static LocalModInfoPopup* create(Mod* mod, ModListLayer* list);
|
||||||
};
|
};
|
||||||
|
|
||||||
class IndexItemInfoPopup : public ModInfoPopup, public FLAlertLayerProtocol {
|
class IndexItemInfoPopup : public ModInfoPopup {
|
||||||
protected:
|
protected:
|
||||||
IndexItemHandle m_item;
|
IndexItemHandle m_item;
|
||||||
EventListener<ModInstallFilter> m_installListener;
|
EventListener<ModInstallFilter> m_installListener;
|
||||||
|
@ -99,9 +99,6 @@ protected:
|
||||||
void onInstallProgress(ModInstallEvent* event);
|
void onInstallProgress(ModInstallEvent* event);
|
||||||
void onInstall(CCObject*);
|
void onInstall(CCObject*);
|
||||||
void onCancel(CCObject*);
|
void onCancel(CCObject*);
|
||||||
void doInstall();
|
|
||||||
|
|
||||||
void FLAlert_Clicked(FLAlertLayer*, bool) override;
|
|
||||||
|
|
||||||
CCNode* createLogo(CCSize const& size) override;
|
CCNode* createLogo(CCSize const& size) override;
|
||||||
ModMetadata getMetadata() const override;
|
ModMetadata getMetadata() const override;
|
||||||
|
|
327
loader/src/ui/internal/list/InstallListCell.cpp
Normal file
327
loader/src/ui/internal/list/InstallListCell.cpp
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
#include "InstallListCell.hpp"
|
||||||
|
#include "InstallListPopup.hpp"
|
||||||
|
#include <Geode/binding/ButtonSprite.hpp>
|
||||||
|
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||||
|
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||||
|
#include <Geode/binding/FLAlertLayer.hpp>
|
||||||
|
#include <Geode/binding/StatsCell.hpp>
|
||||||
|
#include <Geode/ui/GeodeUI.hpp>
|
||||||
|
#include <loader/LoaderImpl.hpp>
|
||||||
|
#include <utility>
|
||||||
|
#include "../info/TagNode.hpp"
|
||||||
|
#include "../info/DevProfilePopup.hpp"
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static bool tryOrAlert(Result<T> const& res, char const* title) {
|
||||||
|
if (!res) {
|
||||||
|
FLAlertLayer::create(title, res.unwrapErr(), "OK")->show();
|
||||||
|
}
|
||||||
|
return res.isOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallListCell
|
||||||
|
|
||||||
|
void InstallListCell::draw() {
|
||||||
|
reinterpret_cast<StatsCell*>(this)->StatsCell::draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
float InstallListCell::getLogoSize() const {
|
||||||
|
return m_height / 1.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallListCell::setupInfo(
|
||||||
|
std::string name,
|
||||||
|
std::optional<std::string> developer,
|
||||||
|
std::variant<VersionInfo, ComparableVersionInfo> version,
|
||||||
|
bool inactive
|
||||||
|
) {
|
||||||
|
m_menu = CCMenu::create();
|
||||||
|
m_menu->setPosition(m_width - 10.f, m_height / 2);
|
||||||
|
this->addChild(m_menu);
|
||||||
|
|
||||||
|
auto logoSize = this->getLogoSize();
|
||||||
|
|
||||||
|
auto logoSpr = this->createLogo({ logoSize, logoSize });
|
||||||
|
logoSpr->setPosition({ logoSize / 2 + 12.f, m_height / 2 });
|
||||||
|
auto logoSprColor = typeinfo_cast<CCRGBAProtocol*>(logoSpr);
|
||||||
|
if (inactive && logoSprColor) {
|
||||||
|
logoSprColor->setColor({ 163, 163, 163 });
|
||||||
|
}
|
||||||
|
this->addChild(logoSpr);
|
||||||
|
|
||||||
|
auto titleLabel = CCLabelBMFont::create(name.c_str(), "bigFont.fnt");
|
||||||
|
titleLabel->setAnchorPoint({ .0f, .5f });
|
||||||
|
titleLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f);
|
||||||
|
titleLabel->setPositionY(m_height / 2);
|
||||||
|
titleLabel->limitLabelWidth(m_width / 2 - 70.f, .4f, .1f);
|
||||||
|
if (inactive) {
|
||||||
|
titleLabel->setColor({ 163, 163, 163 });
|
||||||
|
}
|
||||||
|
this->addChild(titleLabel);
|
||||||
|
|
||||||
|
m_developerBtn = nullptr;
|
||||||
|
if (developer) {
|
||||||
|
auto creatorStr = "by " + *developer;
|
||||||
|
auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt");
|
||||||
|
creatorLabel->setScale(.34f);
|
||||||
|
if (inactive) {
|
||||||
|
creatorLabel->setColor({ 163, 163, 163 });
|
||||||
|
}
|
||||||
|
|
||||||
|
m_developerBtn = CCMenuItemSpriteExtra::create(
|
||||||
|
creatorLabel, this, menu_selector(InstallListCell::onViewDev)
|
||||||
|
);
|
||||||
|
m_developerBtn->setPosition(
|
||||||
|
titleLabel->getPositionX() + titleLabel->getScaledContentSize().width + 3.f +
|
||||||
|
creatorLabel->getScaledContentSize().width / 2 -
|
||||||
|
m_menu->getPositionX(),
|
||||||
|
-0.5f
|
||||||
|
);
|
||||||
|
m_menu->addChild(m_developerBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto versionLabel = CCLabelBMFont::create(
|
||||||
|
std::holds_alternative<VersionInfo>(version) ?
|
||||||
|
std::get<VersionInfo>(version).toString(false).c_str() :
|
||||||
|
std::get<ComparableVersionInfo>(version).toString().c_str(),
|
||||||
|
"bigFont.fnt"
|
||||||
|
);
|
||||||
|
versionLabel->setAnchorPoint({ .0f, .5f });
|
||||||
|
versionLabel->setScale(.2f);
|
||||||
|
versionLabel->setPosition(
|
||||||
|
titleLabel->getPositionX() + titleLabel->getScaledContentSize().width + 3.f +
|
||||||
|
(m_developerBtn ? m_developerBtn->getScaledContentSize().width + 3.f : 0.f),
|
||||||
|
titleLabel->getPositionY() - 1.f
|
||||||
|
);
|
||||||
|
versionLabel->setColor({ 0, 255, 0 });
|
||||||
|
if (inactive) {
|
||||||
|
versionLabel->setColor({ 0, 163, 0 });
|
||||||
|
}
|
||||||
|
this->addChild(versionLabel);
|
||||||
|
|
||||||
|
if (!std::holds_alternative<VersionInfo>(version)) return;
|
||||||
|
if (auto tag = std::get<VersionInfo>(version).getTag()) {
|
||||||
|
auto tagLabel = TagNode::create(tag->toString());
|
||||||
|
tagLabel->setAnchorPoint({.0f, .5f});
|
||||||
|
tagLabel->setScale(.2f);
|
||||||
|
tagLabel->setPosition(
|
||||||
|
versionLabel->getPositionX() + versionLabel->getScaledContentSize().width + 3.f,
|
||||||
|
versionLabel->getPositionY()
|
||||||
|
);
|
||||||
|
this->addChild(tagLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallListCell::setupInfo(ModMetadata const& metadata, bool inactive) {
|
||||||
|
this->setupInfo(metadata.getName(), metadata.getDeveloper(), metadata.getVersion(), inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallListCell::onViewDev(CCObject*) {
|
||||||
|
DevProfilePopup::create(getDeveloper())->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstallListCell::init(InstallListPopup* list, CCSize const& size) {
|
||||||
|
m_width = size.width;
|
||||||
|
m_height = size.height;
|
||||||
|
m_layer = list;
|
||||||
|
this->setContentSize(size);
|
||||||
|
this->setID("install-list-cell");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstallListCell::isIncluded() {
|
||||||
|
return m_toggle && m_toggle->isOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModInstallListCell
|
||||||
|
|
||||||
|
bool ModInstallListCell::init(Mod* mod, InstallListPopup* list, CCSize const& size) {
|
||||||
|
if (!InstallListCell::init(list, size))
|
||||||
|
return false;
|
||||||
|
m_mod = mod;
|
||||||
|
this->setupInfo(mod->getMetadata(), true);
|
||||||
|
auto message = CCLabelBMFont::create("Installed", "bigFont.fnt");
|
||||||
|
message->setAnchorPoint({ 1.f, .5f });
|
||||||
|
message->setPositionX(m_menu->getPositionX());
|
||||||
|
message->setPositionY(16.f);
|
||||||
|
message->setScale(0.4f);
|
||||||
|
message->setColor({ 163, 163, 163 });
|
||||||
|
this->addChild(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModInstallListCell* ModInstallListCell::create(Mod* mod, InstallListPopup* list, CCSize const& size) {
|
||||||
|
auto ret = new ModInstallListCell();
|
||||||
|
if (ret->init(mod, list, size)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
CC_SAFE_DELETE(ret);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCNode* ModInstallListCell::createLogo(CCSize const& size) {
|
||||||
|
return geode::createModLogo(m_mod, size);
|
||||||
|
}
|
||||||
|
std::string ModInstallListCell::getID() const {
|
||||||
|
return m_mod->getID();
|
||||||
|
}
|
||||||
|
std::string ModInstallListCell::getDeveloper() const {
|
||||||
|
return m_mod->getDeveloper();
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexItemInstallListCell
|
||||||
|
|
||||||
|
bool IndexItemInstallListCell::init(
|
||||||
|
IndexItemHandle item,
|
||||||
|
ModMetadata::Dependency::Importance importance,
|
||||||
|
InstallListPopup* list,
|
||||||
|
CCSize const& size,
|
||||||
|
std::optional<bool> selected
|
||||||
|
) {
|
||||||
|
if (!InstallListCell::init(list, size))
|
||||||
|
return false;
|
||||||
|
m_item = item;
|
||||||
|
this->setupInfo(item->getMetadata(), item->isInstalled());
|
||||||
|
if (item->isInstalled()) {
|
||||||
|
auto message = CCLabelBMFont::create("Installed", "bigFont.fnt");
|
||||||
|
message->setAnchorPoint({ 1.f, .5f });
|
||||||
|
message->setPositionX(m_menu->getPositionX());
|
||||||
|
message->setPositionY(16.f);
|
||||||
|
message->setScale(0.4f);
|
||||||
|
message->setColor({ 163, 163, 163 });
|
||||||
|
this->addChild(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_toggle = CCMenuItemToggler::createWithStandardSprites(
|
||||||
|
m_layer,
|
||||||
|
menu_selector(InstallListPopup::onCellToggle),
|
||||||
|
.6f
|
||||||
|
);
|
||||||
|
m_toggle->setPosition(-m_toggle->getScaledContentSize().width / 2, 0.f);
|
||||||
|
|
||||||
|
switch (importance) {
|
||||||
|
case ModMetadata::Dependency::Importance::Required:
|
||||||
|
m_toggle->setClickable(false);
|
||||||
|
m_toggle->toggle(true);
|
||||||
|
break;
|
||||||
|
case ModMetadata::Dependency::Importance::Recommended:
|
||||||
|
m_toggle->setClickable(true);
|
||||||
|
m_toggle->toggle(true);
|
||||||
|
break;
|
||||||
|
case ModMetadata::Dependency::Importance::Suggested:
|
||||||
|
m_toggle->setClickable(true);
|
||||||
|
m_toggle->toggle(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_item->getAvailablePlatforms().count(GEODE_PLATFORM_TARGET) == 0) {
|
||||||
|
m_toggle->setClickable(false);
|
||||||
|
m_toggle->toggle(false);
|
||||||
|
|
||||||
|
auto message = CCLabelBMFont::create("N/A", "bigFont.fnt");
|
||||||
|
message->setAnchorPoint({ 1.f, .5f });
|
||||||
|
message->setPositionX(m_menu->getPositionX() - m_toggle->getScaledContentSize().width - 5.f);
|
||||||
|
message->setPositionY(16.f);
|
||||||
|
message->setScale(0.4f);
|
||||||
|
message->setColor({ 240, 31, 31 });
|
||||||
|
this->addChild(message);
|
||||||
|
|
||||||
|
if (importance != ModMetadata::Dependency::Importance::Required) {
|
||||||
|
message->setCString("N/A (Optional)");
|
||||||
|
message->setColor({ 120, 15, 15 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_toggle->m_notClickable) {
|
||||||
|
m_toggle->m_offButton->setOpacity(100);
|
||||||
|
m_toggle->m_offButton->setColor(cc3x(155));
|
||||||
|
m_toggle->m_onButton->setOpacity(100);
|
||||||
|
m_toggle->m_onButton->setColor(cc3x(155));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_toggle->m_notClickable && selected) {
|
||||||
|
m_toggle->toggle(*selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_menu->addChild(m_toggle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexItemInstallListCell* IndexItemInstallListCell::create(
|
||||||
|
IndexItemHandle item,
|
||||||
|
ModMetadata::Dependency::Importance importance,
|
||||||
|
InstallListPopup* list,
|
||||||
|
CCSize const& size,
|
||||||
|
std::optional<bool> selected
|
||||||
|
) {
|
||||||
|
auto ret = new IndexItemInstallListCell();
|
||||||
|
if (ret->init(std::move(item), importance, list, size, selected)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
CC_SAFE_DELETE(ret);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCNode* IndexItemInstallListCell::createLogo(CCSize const& size) {
|
||||||
|
return geode::createIndexItemLogo(m_item, size);
|
||||||
|
}
|
||||||
|
std::string IndexItemInstallListCell::getID() const {
|
||||||
|
return m_item->getMetadata().getID();
|
||||||
|
}
|
||||||
|
std::string IndexItemInstallListCell::getDeveloper() const {
|
||||||
|
return m_item->getMetadata().getDeveloper();
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexItemHandle IndexItemInstallListCell::getItem() {
|
||||||
|
return m_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnknownInstallListCell
|
||||||
|
|
||||||
|
bool UnknownInstallListCell::init(
|
||||||
|
ModMetadata::Dependency const& dependency,
|
||||||
|
InstallListPopup* list,
|
||||||
|
CCSize const& size
|
||||||
|
) {
|
||||||
|
if (!InstallListCell::init(list, size))
|
||||||
|
return false;
|
||||||
|
m_dependency = dependency;
|
||||||
|
bool optional = dependency.importance != ModMetadata::Dependency::Importance::Required;
|
||||||
|
this->setupInfo(dependency.id, std::nullopt, dependency.version, optional);
|
||||||
|
auto message = CCLabelBMFont::create("Missing", "bigFont.fnt");
|
||||||
|
message->setAnchorPoint({ 1.f, .5f });
|
||||||
|
message->setPositionX(m_menu->getPositionX());
|
||||||
|
message->setPositionY(16.f);
|
||||||
|
message->setScale(0.4f);
|
||||||
|
message->setColor({ 240, 31, 31 });
|
||||||
|
if (optional) {
|
||||||
|
message->setCString("Missing (Optional)");
|
||||||
|
message->setColor({ 120, 15, 15 });
|
||||||
|
}
|
||||||
|
this->addChild(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnknownInstallListCell* UnknownInstallListCell::create(
|
||||||
|
ModMetadata::Dependency const& dependency,
|
||||||
|
InstallListPopup* list,
|
||||||
|
CCSize const& size
|
||||||
|
) {
|
||||||
|
auto ret = new UnknownInstallListCell();
|
||||||
|
if (ret->init(dependency, list, size)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
CC_SAFE_DELETE(ret);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCNode* UnknownInstallListCell::createLogo(CCSize const& size) {
|
||||||
|
return geode::createDefaultLogo(size);
|
||||||
|
}
|
||||||
|
std::string UnknownInstallListCell::getID() const {
|
||||||
|
return m_dependency.id;
|
||||||
|
}
|
||||||
|
std::string UnknownInstallListCell::getDeveloper() const {
|
||||||
|
return "";
|
||||||
|
}
|
114
loader/src/ui/internal/list/InstallListCell.hpp
Normal file
114
loader/src/ui/internal/list/InstallListCell.hpp
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Geode/binding/TableViewCell.hpp>
|
||||||
|
#include <Geode/binding/FLAlertLayerProtocol.hpp>
|
||||||
|
#include <Geode/loader/Loader.hpp>
|
||||||
|
#include <Geode/loader/ModMetadata.hpp>
|
||||||
|
#include <Geode/loader/Index.hpp>
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
class InstallListPopup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for install list items
|
||||||
|
*/
|
||||||
|
class InstallListCell : public CCLayer {
|
||||||
|
protected:
|
||||||
|
float m_width;
|
||||||
|
float m_height;
|
||||||
|
InstallListPopup* m_layer;
|
||||||
|
CCMenu* m_menu;
|
||||||
|
CCMenuItemSpriteExtra* m_developerBtn;
|
||||||
|
CCMenuItemToggler* m_toggle = nullptr;
|
||||||
|
|
||||||
|
void setupInfo(
|
||||||
|
std::string name,
|
||||||
|
std::optional<std::string> developer,
|
||||||
|
std::variant<VersionInfo, ComparableVersionInfo> version,
|
||||||
|
bool inactive
|
||||||
|
);
|
||||||
|
|
||||||
|
bool init(InstallListPopup* list, CCSize const& size);
|
||||||
|
void setupInfo(ModMetadata const& metadata, bool inactive);
|
||||||
|
void draw() override;
|
||||||
|
|
||||||
|
float getLogoSize() const;
|
||||||
|
void onViewDev(CCObject*);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isIncluded();
|
||||||
|
|
||||||
|
virtual CCNode* createLogo(CCSize const& size) = 0;
|
||||||
|
[[nodiscard]] virtual std::string getID() const = 0;
|
||||||
|
[[nodiscard]] virtual std::string getDeveloper() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install list item for a mod
|
||||||
|
*/
|
||||||
|
class ModInstallListCell : public InstallListCell {
|
||||||
|
protected:
|
||||||
|
Mod* m_mod;
|
||||||
|
|
||||||
|
bool init(Mod* mod, InstallListPopup* list, CCSize const& size);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ModInstallListCell* create(Mod* mod, InstallListPopup* list, CCSize const& size);
|
||||||
|
|
||||||
|
CCNode* createLogo(CCSize const& size) override;
|
||||||
|
[[nodiscard]] std::string getID() const override;
|
||||||
|
[[nodiscard]] std::string getDeveloper() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install list item for an index item
|
||||||
|
*/
|
||||||
|
class IndexItemInstallListCell : public InstallListCell {
|
||||||
|
protected:
|
||||||
|
IndexItemHandle m_item;
|
||||||
|
|
||||||
|
bool init(
|
||||||
|
IndexItemHandle item,
|
||||||
|
ModMetadata::Dependency::Importance importance,
|
||||||
|
InstallListPopup* list,
|
||||||
|
CCSize const& size,
|
||||||
|
std::optional<bool> selected
|
||||||
|
);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static IndexItemInstallListCell* create(
|
||||||
|
IndexItemHandle item,
|
||||||
|
ModMetadata::Dependency::Importance importance,
|
||||||
|
InstallListPopup* list,
|
||||||
|
CCSize const& size,
|
||||||
|
std::optional<bool> selected
|
||||||
|
);
|
||||||
|
|
||||||
|
CCNode* createLogo(CCSize const& size) override;
|
||||||
|
[[nodiscard]] std::string getID() const override;
|
||||||
|
[[nodiscard]] std::string getDeveloper() const override;
|
||||||
|
|
||||||
|
IndexItemHandle getItem();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install list item for an unknown item
|
||||||
|
*/
|
||||||
|
class UnknownInstallListCell : public InstallListCell {
|
||||||
|
protected:
|
||||||
|
ModMetadata::Dependency m_dependency;
|
||||||
|
|
||||||
|
bool init(ModMetadata::Dependency const& dependency, InstallListPopup* list, CCSize const& size);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static UnknownInstallListCell* create(
|
||||||
|
ModMetadata::Dependency const& dependency,
|
||||||
|
InstallListPopup* list,
|
||||||
|
CCSize const& size
|
||||||
|
);
|
||||||
|
|
||||||
|
CCNode* createLogo(CCSize const& size) override;
|
||||||
|
[[nodiscard]] std::string getID() const override;
|
||||||
|
[[nodiscard]] std::string getDeveloper() const override;
|
||||||
|
};
|
229
loader/src/ui/internal/list/InstallListPopup.cpp
Normal file
229
loader/src/ui/internal/list/InstallListPopup.cpp
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
#include "InstallListPopup.hpp"
|
||||||
|
#include "InstallListCell.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
bool InstallListPopup::setup(IndexItemHandle item, MiniFunction<void(IndexInstallList const&)> callback) {
|
||||||
|
m_noElasticity = true;
|
||||||
|
|
||||||
|
m_item = item;
|
||||||
|
m_callback = callback;
|
||||||
|
|
||||||
|
this->setTitle("Select Mods to Install");
|
||||||
|
|
||||||
|
this->createList();
|
||||||
|
|
||||||
|
auto installBtnSpr = IconButtonSprite::create(
|
||||||
|
"GE_button_01.png"_spr,
|
||||||
|
CCSprite::createWithSpriteFrameName("install.png"_spr),
|
||||||
|
"Install",
|
||||||
|
"bigFont.fnt"
|
||||||
|
);
|
||||||
|
installBtnSpr->setScale(.6f);
|
||||||
|
|
||||||
|
auto installBtn = CCMenuItemSpriteExtra::create(
|
||||||
|
installBtnSpr,
|
||||||
|
this,
|
||||||
|
menu_selector(InstallListPopup::onInstall)
|
||||||
|
);
|
||||||
|
installBtn->setPositionY(-m_bgSprite->getScaledContentSize().height / 2 + 22.f);
|
||||||
|
m_buttonMenu->addChild(installBtn);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallListPopup::createList() {
|
||||||
|
auto winSize = CCDirector::sharedDirector()->getWinSize();
|
||||||
|
|
||||||
|
std::unordered_map<std::string, InstallListCell*> oldCells;
|
||||||
|
bool oldScrollAtBottom;
|
||||||
|
std::optional<float> oldScroll;
|
||||||
|
if (m_list) {
|
||||||
|
CCArray* oldEntries = m_list->m_entries;
|
||||||
|
for (size_t i = 0; i < oldEntries->count(); i++) {
|
||||||
|
auto* itemCell = typeinfo_cast<InstallListCell*>(oldEntries->objectAtIndex(i));
|
||||||
|
oldCells[itemCell->getID()] = itemCell;
|
||||||
|
}
|
||||||
|
auto content = m_list->m_tableView->m_contentLayer;
|
||||||
|
oldScroll = content->getPositionY();
|
||||||
|
oldScrollAtBottom = oldScroll >= 0.f;
|
||||||
|
if (!oldScrollAtBottom)
|
||||||
|
*oldScroll += content->getScaledContentSize().height;
|
||||||
|
m_list->removeFromParent();
|
||||||
|
}
|
||||||
|
if (m_listParent) {
|
||||||
|
m_listParent->removeFromParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_listParent = CCNode::create();
|
||||||
|
m_mainLayer->addChild(m_listParent);
|
||||||
|
|
||||||
|
auto items = this->createCells(oldCells);
|
||||||
|
m_list = ListView::create(
|
||||||
|
items,
|
||||||
|
this->getCellSize().height,
|
||||||
|
this->getListSize().width,
|
||||||
|
this->getListSize().height
|
||||||
|
);
|
||||||
|
m_list->setPosition(winSize / 2 - m_list->getScaledContentSize() / 2);
|
||||||
|
m_listParent->addChild(m_list);
|
||||||
|
|
||||||
|
// restore scroll on list recreation
|
||||||
|
// it's stored from the top unless was scrolled all the way to the bottom
|
||||||
|
if (oldScroll) {
|
||||||
|
auto content = m_list->m_tableView->m_contentLayer;
|
||||||
|
if (oldScrollAtBottom)
|
||||||
|
content->setPositionY(*oldScroll);
|
||||||
|
else
|
||||||
|
content->setPositionY(*oldScroll - content->getScaledContentSize().height);
|
||||||
|
}
|
||||||
|
|
||||||
|
addListBorders(m_listParent, winSize / 2, m_list->getScaledContentSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
CCArray* InstallListPopup::createCells(std::unordered_map<std::string, InstallListCell*> const& oldCells) {
|
||||||
|
std::vector<InstallListCell*> top;
|
||||||
|
std::vector<InstallListCell*> middle;
|
||||||
|
std::vector<InstallListCell*> bottom;
|
||||||
|
|
||||||
|
std::queue<ModMetadata::Dependency> queue;
|
||||||
|
std::unordered_set<std::string> queued;
|
||||||
|
|
||||||
|
auto id = m_item->getMetadata().getID();
|
||||||
|
middle.push_back(IndexItemInstallListCell::create(
|
||||||
|
m_item,
|
||||||
|
ModMetadata::Dependency::Importance::Required,
|
||||||
|
this,
|
||||||
|
this->getCellSize(),
|
||||||
|
oldCells.contains(id) ? std::make_optional(oldCells.at(id)->isIncluded()) : std::nullopt
|
||||||
|
));
|
||||||
|
for (auto const& dep : m_item->getMetadata().getDependencies()) {
|
||||||
|
queue.push(dep);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = Index::get();
|
||||||
|
while (!queue.empty()) {
|
||||||
|
auto const& item = queue.front();
|
||||||
|
if (queued.contains(item.id)) {
|
||||||
|
queue.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
queued.insert(item.id);
|
||||||
|
|
||||||
|
// installed
|
||||||
|
if (item.mod && !item.mod->isUninstalled()) {
|
||||||
|
bottom.push_back(ModInstallListCell::create(item.mod, this, this->getCellSize()));
|
||||||
|
for (auto const& dep : item.mod->getMetadata().getDependencies()) {
|
||||||
|
queue.push(dep);
|
||||||
|
}
|
||||||
|
queue.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// on index
|
||||||
|
if (auto depItem = index->getItem(item.id, item.version)) {
|
||||||
|
auto cell = IndexItemInstallListCell::create(
|
||||||
|
depItem,
|
||||||
|
item.importance,
|
||||||
|
this,
|
||||||
|
this->getCellSize(),
|
||||||
|
oldCells.contains(item.id) ?
|
||||||
|
std::make_optional(oldCells.at(item.id)->isIncluded()) :
|
||||||
|
std::nullopt
|
||||||
|
);
|
||||||
|
|
||||||
|
// put missing dependencies at the top
|
||||||
|
if (depItem->getAvailablePlatforms().count(GEODE_PLATFORM_TARGET) == 0)
|
||||||
|
top.push_back(cell);
|
||||||
|
// put installed dependencies at the bottom
|
||||||
|
else if (depItem->isInstalled())
|
||||||
|
bottom.push_back(cell);
|
||||||
|
else
|
||||||
|
middle.push_back(cell);
|
||||||
|
|
||||||
|
if (!cell->isIncluded()) {
|
||||||
|
queue.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& dep : depItem->getMetadata().getDependencies()) {
|
||||||
|
queue.push(dep);
|
||||||
|
}
|
||||||
|
queue.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown (aka not installed and missing from index)
|
||||||
|
auto unknownCell = UnknownInstallListCell::create(item, this, this->getCellSize());
|
||||||
|
top.push_back(unknownCell);
|
||||||
|
queue.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mods = CCArray::create();
|
||||||
|
for (auto const& item : top) {
|
||||||
|
mods->addObject(item);
|
||||||
|
}
|
||||||
|
for (auto const& item : middle) {
|
||||||
|
mods->addObject(item);
|
||||||
|
}
|
||||||
|
for (auto const& item : bottom) {
|
||||||
|
mods->addObject(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
|
||||||
|
CCSize InstallListPopup::getListSize() const {
|
||||||
|
return { 340.f, 170.f };
|
||||||
|
}
|
||||||
|
|
||||||
|
CCSize InstallListPopup::getCellSize() const {
|
||||||
|
return { getListSize().width, 30.f };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
|
||||||
|
void InstallListPopup::onCellToggle(cocos2d::CCObject* obj) {
|
||||||
|
auto* toggler = typeinfo_cast<CCMenuItemToggler*>(obj);
|
||||||
|
if (toggler && !toggler->m_notClickable)
|
||||||
|
toggler->toggle(!toggler->isOn());
|
||||||
|
this->createList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallListPopup::onInstall(cocos2d::CCObject* obj) {
|
||||||
|
this->onBtn2(obj);
|
||||||
|
if (!m_callback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IndexInstallList list;
|
||||||
|
list.target = m_item;
|
||||||
|
|
||||||
|
CCArray* entries = m_list->m_entries;
|
||||||
|
for (size_t i = entries->count(); i > 0; i--) {
|
||||||
|
auto* itemCell = typeinfo_cast<IndexItemInstallListCell*>(entries->objectAtIndex(i - 1));
|
||||||
|
if (!itemCell || !itemCell->isIncluded())
|
||||||
|
continue;
|
||||||
|
IndexItemHandle item = itemCell->getItem();
|
||||||
|
list.list.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_callback(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static
|
||||||
|
|
||||||
|
InstallListPopup* InstallListPopup::create(
|
||||||
|
IndexItemHandle item,
|
||||||
|
MiniFunction<void(IndexInstallList const&)> onInstall
|
||||||
|
) {
|
||||||
|
auto ret = new InstallListPopup();
|
||||||
|
if (!ret->init(380.f, 250.f, std::move(item), std::move(onInstall))) {
|
||||||
|
CC_SAFE_DELETE(ret);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ret->autorelease();
|
||||||
|
return ret;
|
||||||
|
}
|
30
loader/src/ui/internal/list/InstallListPopup.hpp
Normal file
30
loader/src/ui/internal/list/InstallListPopup.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Geode/ui/Popup.hpp>
|
||||||
|
#include <Geode/loader/Index.hpp>
|
||||||
|
|
||||||
|
#include "InstallListCell.hpp"
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
class InstallListPopup : public Popup<IndexItemHandle, MiniFunction<void(IndexInstallList const&)>> {
|
||||||
|
protected:
|
||||||
|
IndexItemHandle m_item;
|
||||||
|
CCNode* m_listParent;
|
||||||
|
ListView* m_list;
|
||||||
|
MiniFunction<void(IndexInstallList const&)> m_callback;
|
||||||
|
|
||||||
|
bool setup(IndexItemHandle item, MiniFunction<void(IndexInstallList const&)> callback) override;
|
||||||
|
|
||||||
|
void createList();
|
||||||
|
CCArray* createCells(std::unordered_map<std::string, InstallListCell*> const& oldCells);
|
||||||
|
CCSize getCellSize() const;
|
||||||
|
CCSize getListSize() const;
|
||||||
|
|
||||||
|
void onInstall(CCObject* obj);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void onCellToggle(CCObject* obj);
|
||||||
|
|
||||||
|
static InstallListPopup* create(IndexItemHandle item, MiniFunction<void(IndexInstallList const&)> onInstall);
|
||||||
|
};
|
Loading…
Reference in a new issue