Implement UI for multiple version downloading (very cursed)

This commit is contained in:
altalk23 2023-09-05 02:22:57 +03:00
parent 63027a7f84
commit 5d15eb0215
9 changed files with 399 additions and 205 deletions

View file

@ -178,12 +178,22 @@ namespace geode {
* Get all featured index items
*/
std::vector<IndexItemHandle> getFeaturedItems() const;
/**
* Get all latest index items
*/
std::vector<IndexItemHandle> getLatestItems() const;
/**
* Get all index items by a developer
*/
std::vector<IndexItemHandle> getItemsByDeveloper(
std::string const& name
) const;
/**
* Get all index items for a specific mod
*/
std::vector<IndexItemHandle> getItemsByModID(
std::string const& modID
) const;
/**
* Check if an item with this ID is found on the index, and optionally
* provide the version sought after

View file

@ -183,7 +183,14 @@ Result<IndexItemHandle> IndexItem::Impl::create(ghc::filesystem::path const& roo
}
bool IndexItem::Impl::isInstalled() const {
return ghc::filesystem::exists(dirs::getModsDir() / (m_metadata.getID() + ".geode"));
if (!Loader::get()->isModInstalled(m_metadata.getID())) {
return false;
}
auto installed = Loader::get()->getInstalledMod(m_metadata.getID());
if (installed->getVersion() != m_metadata.getVersion()) {
return false;
}
return true;
}
// Helpers
@ -449,6 +456,14 @@ std::vector<IndexItemHandle> Index::getItems() const {
return res;
}
std::vector<IndexItemHandle> Index::getLatestItems() const {
std::vector<IndexItemHandle> res;
for (auto& [modID, versions] : m_impl->m_items) {
res.push_back(this->getMajorItem(modID));
}
return res;
}
std::vector<IndexItemHandle> Index::getFeaturedItems() const {
std::vector<IndexItemHandle> res;
for (auto& items : map::values(m_impl->m_items)) {
@ -475,6 +490,18 @@ std::vector<IndexItemHandle> Index::getItemsByDeveloper(
return res;
}
std::vector<IndexItemHandle> Index::getItemsByModID(
std::string const& modID
) const {
std::vector<IndexItemHandle> res;
if (m_impl->m_items.count(modID)) {
for (auto& [versionStr, item] : m_impl->m_items[modID]) {
res.push_back(item);
}
}
return res;
}
bool Index::isKnownItem(
std::string const& id,
std::optional<VersionInfo> version
@ -758,6 +785,10 @@ void Index::cancelInstall(IndexItemHandle item) {
}
void Index::install(IndexInstallList const& list) {
if (list.list.empty()) {
ModInstallEvent(list.target->getMetadata().getID(), UpdateFinished()).post();
return;
}
Loader::get()->queueInMainThread([this, list]() {
m_impl->installNext(0, list);
});

View file

@ -277,6 +277,95 @@ void ModInfoPopup::setInstallStatus(std::optional<UpdateProgress> const& progres
}
}
void ModInfoPopup::popupInstallItem(IndexItemHandle item) {
auto deps = item->getMetadata().getDependencies();
enum class DepState {
None,
HasOnlyRequired,
HasOptional
} depState = DepState::None;
for (auto const& dep : deps) {
// resolved means it's already installed, so
// no need to ask the user whether they want to install it
if (Loader::get()->isModLoaded(dep.id))
continue;
if (dep.importance != ModMetadata::Dependency::Importance::Required) {
depState = DepState::HasOptional;
break;
}
depState = DepState::HasOnlyRequired;
}
std::string content;
char const* btn1;
char const* btn2;
switch (depState) {
case DepState::None:
content = fmt::format(
"Are you sure you want to install <cg>{}</c>?",
item->getMetadata().getName()
);
btn1 = "Info";
btn2 = "Install";
break;
case DepState::HasOnlyRequired:
content =
"Installing this mod requires other mods to be installed. "
"Would you like to <cy>proceed</c> with the installation or "
"<cb>view</c> which mods are going to be installed?";
btn1 = "View";
btn2 = "Proceed";
break;
case DepState::HasOptional:
content =
"This mod recommends installing other mods alongside it. "
"Would you like to continue with <cy>recommended settings</c> or "
"<cb>customize</c> which mods to install?";
btn1 = "Customize";
btn2 = "Recommended";
break;
}
createQuickPopup("Confirm Install", content, btn1, btn2, 320.f, [&](FLAlertLayer*, bool btn2) {
if (btn2) {
auto canInstall = Index::get()->canInstall(m_item);
if (!canInstall) {
FLAlertLayer::create(
"Unable to Install",
canInstall.unwrapErr(),
"OK"
)->show();
return;
}
this->preInstall();
Index::get()->install(m_item);
}
else {
InstallListPopup::create(m_item, [&](IndexInstallList const& list) {
this->preInstall();
Index::get()->install(list);
})->show();
}
}, true, true);
}
void ModInfoPopup::preInstall() {
if (m_latestVersionLabel) {
m_latestVersionLabel->setVisible(false);
}
this->setInstallStatus(UpdateProgress(0, "Starting install"));
m_installBtn->setTarget(
this, menu_selector(ModInfoPopup::onCancelInstall)
);
m_installBtnSpr->setString("Cancel");
m_installBtnSpr->setBG("GJ_button_06.png", false);
}
void ModInfoPopup::onCancelInstall(CCObject*) {
Index::get()->cancelInstall(m_item);
}
// LocalModInfoPopup
LocalModInfoPopup::LocalModInfoPopup()
: m_installListener(
@ -493,61 +582,7 @@ void LocalModInfoPopup::onUpdateProgress(ModInstallEvent* event) {
}
void LocalModInfoPopup::onUpdate(CCObject*) {
auto list = Index::get()->getInstallList(m_item);
if (!list) {
return FLAlertLayer::create(
"Unable to Update",
list.unwrapErr(),
"OK"
)->show();
}
auto layer = FLAlertLayer::create(
this,
"Confirm Update",
fmt::format(
"The following mods will be updated:\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"
);
layer->setTag(TAG_CONFIRM_UPDATE);
layer->show();
}
void LocalModInfoPopup::onCancel(CCObject*) {
Index::get()->cancelInstall(m_item);
}
void LocalModInfoPopup::doUpdate() {
if (m_latestVersionLabel) {
m_latestVersionLabel->setVisible(false);
}
if (m_minorVersionLabel) {
m_minorVersionLabel->setVisible(false);
}
this->setInstallStatus(UpdateProgress(0, "Starting update"));
m_installBtn->setTarget(
this, menu_selector(LocalModInfoPopup::onCancel)
);
m_installBtnSpr->setString("Cancel");
m_installBtnSpr->setBG("GJ_button_06.png", false);
Index::get()->install(m_item);
this->popupInstallItem(m_item);
}
void LocalModInfoPopup::onUninstall(CCObject*) {
@ -631,12 +666,6 @@ void LocalModInfoPopup::FLAlert_Clicked(FLAlertLayer* layer, bool btn2) {
}
this->onClose(nullptr);
} break;
case TAG_CONFIRM_UPDATE: {
if (btn2) {
this->doUpdate();
}
} break;
}
}
@ -687,7 +716,8 @@ bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
if (!ModInfoPopup::init(item->getMetadata(), list)) return false;
if (item->isInstalled()) return true;
// bruh why is this here if we are allowing for browsing already installed mods
// if (item->isInstalled()) return true;
m_installBtnSpr = IconButtonSprite::create(
"GE_button_01.png"_spr,
@ -750,92 +780,7 @@ void IndexItemInfoPopup::onInstallProgress(ModInstallEvent* event) {
}
void IndexItemInfoPopup::onInstall(CCObject*) {
auto deps = m_item->getMetadata().getDependencies();
enum class DepState {
None,
HasOnlyRequired,
HasOptional
} depState = DepState::None;
for (auto const& item : deps) {
// resolved means it's already installed, so
// no need to ask the user whether they want to install it
if (Loader::get()->isModLoaded(item.id))
continue;
if (item.importance != ModMetadata::Dependency::Importance::Required) {
depState = DepState::HasOptional;
break;
}
depState = DepState::HasOnlyRequired;
}
std::string content;
char const* btn1;
char const* btn2;
switch (depState) {
case DepState::None:
content = fmt::format(
"Are you sure you want to install <cg>{}</c>?",
m_item->getMetadata().getName()
);
btn1 = "Info";
btn2 = "Install";
break;
case DepState::HasOnlyRequired:
content =
"Installing this mod requires other mods to be installed. "
"Would you like to <cy>proceed</c> with the installation or "
"<cb>view</c> which mods are going to be installed?";
btn1 = "View";
btn2 = "Proceed";
break;
case DepState::HasOptional:
content =
"This mod recommends installing other mods alongside it. "
"Would you like to continue with <cy>recommended settings</c> or "
"<cb>customize</c> which mods to install?";
btn1 = "Customize";
btn2 = "Recommended";
break;
}
createQuickPopup("Confirm Install", content, btn1, btn2, 320.f, [&](FLAlertLayer*, bool btn2) {
if (btn2) {
auto canInstall = Index::get()->canInstall(m_item);
if (!canInstall) {
FLAlertLayer::create(
"Unable to Install",
canInstall.unwrapErr(),
"OK"
)->show();
return;
}
this->preInstall();
Index::get()->install(m_item);
}
else {
InstallListPopup::create(m_item, [&](IndexInstallList const& list) {
this->preInstall();
Index::get()->install(list);
})->show();
}
}, true, true);
}
void IndexItemInfoPopup::preInstall() {
if (m_latestVersionLabel) {
m_latestVersionLabel->setVisible(false);
}
this->setInstallStatus(UpdateProgress(0, "Starting install"));
m_installBtn->setTarget(
this, menu_selector(IndexItemInfoPopup::onCancel)
);
m_installBtnSpr->setString("Cancel");
m_installBtnSpr->setBG("GJ_button_06.png", false);
}
void IndexItemInfoPopup::onCancel(CCObject*) {
Index::get()->cancelInstall(m_item);
this->popupInstallItem(m_item);
}
CCNode* IndexItemInfoPopup::createLogo(CCSize const& size) {

View file

@ -38,6 +38,7 @@ protected:
MDTextArea* m_detailsArea;
MDTextArea* m_changelogArea = nullptr;
Scrollbar* m_scrollbar;
IndexItemHandle m_item;
void onChangelog(CCObject*);
void onRepository(CCObject*);
@ -51,13 +52,16 @@ protected:
void setInstallStatus(std::optional<UpdateProgress> const& progress);
void popupInstallItem(IndexItemHandle item);
void preInstall();
void onCancelInstall(CCObject*);
virtual CCNode* createLogo(CCSize const& size) = 0;
virtual ModMetadata getMetadata() const = 0;
};
class LocalModInfoPopup : public ModInfoPopup, public FLAlertLayerProtocol {
protected:
IndexItemHandle m_item;
EventListener<ModInstallFilter> m_installListener;
Mod* m_mod;
@ -74,8 +78,6 @@ protected:
void onUpdateProgress(ModInstallEvent* event);
void onUpdate(CCObject*);
void onCancel(CCObject*);
void doUpdate();
void FLAlert_Clicked(FLAlertLayer*, bool) override;
@ -91,16 +93,12 @@ public:
class IndexItemInfoPopup : public ModInfoPopup {
protected:
IndexItemHandle m_item;
EventListener<ModInstallFilter> m_installListener;
bool init(IndexItemHandle item, ModListLayer* list);
void onInstallProgress(ModInstallEvent* event);
void onInstall(CCObject*);
void onCancel(CCObject*);
void preInstall();
CCNode* createLogo(CCSize const& size) override;
ModMetadata getMetadata() const override;

View file

@ -8,7 +8,6 @@
#include <Geode/ui/GeodeUI.hpp>
#include <loader/LoaderImpl.hpp>
#include <utility>
#include "../info/TagNode.hpp"
#include "../info/DevProfilePopup.hpp"
// InstallListCell
@ -27,8 +26,11 @@ void InstallListCell::setupInfo(
std::variant<VersionInfo, ComparableVersionInfo> version,
bool inactive
) {
m_inactive = inactive;
m_menu = CCMenu::create();
m_menu->setPosition(m_width - 10.f, m_height / 2);
m_menu->setPosition(0, 0);
m_menu->setAnchorPoint({ .0f, .0f });
m_menu->setContentSize({m_width, m_height});
this->addChild(m_menu);
auto logoSize = this->getLogoSize();
@ -41,15 +43,15 @@ void InstallListCell::setupInfo(
}
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);
m_titleLabel = CCLabelBMFont::create(name.c_str(), "bigFont.fnt");
m_titleLabel->setAnchorPoint({ .0f, .5f });
m_titleLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f);
m_titleLabel->setPositionY(m_height / 2);
m_titleLabel->limitLabelWidth(m_width / 2 - 70.f, .4f, .1f);
if (inactive) {
titleLabel->setColor({ 163, 163, 163 });
m_titleLabel->setColor({ 163, 163, 163 });
}
this->addChild(titleLabel);
this->addChild(m_titleLabel);
m_developerBtn = nullptr;
if (developer) {
@ -64,43 +66,55 @@ void InstallListCell::setupInfo(
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_titleLabel->getPositionX() + m_titleLabel->getScaledContentSize().width + 3.f +
creatorLabel->getScaledContentSize().width / 2,
m_height / 2
);
m_menu->addChild(m_developerBtn);
}
auto versionLabel = CCLabelBMFont::create(
this->setupVersion(version);
}
void InstallListCell::setupVersion(std::variant<VersionInfo, ComparableVersionInfo> version) {
if (m_versionLabel) {
m_versionLabel->removeFromParent();
m_versionLabel = nullptr;
}
if (m_tagLabel) {
m_tagLabel->removeFromParent();
m_tagLabel = nullptr;
}
m_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_versionLabel->setAnchorPoint({ .0f, .5f });
m_versionLabel->setScale(.2f);
m_versionLabel->setPosition(
m_titleLabel->getPositionX() + m_titleLabel->getScaledContentSize().width + 3.f +
(m_developerBtn ? m_developerBtn->getScaledContentSize().width + 3.f : 0.f),
titleLabel->getPositionY() - 1.f
m_titleLabel->getPositionY() - 1.f
);
versionLabel->setColor({ 0, 255, 0 });
if (inactive) {
versionLabel->setColor({ 0, 163, 0 });
m_versionLabel->setColor({ 0, 255, 0 });
if (m_inactive) {
m_versionLabel->setColor({ 0, 163, 0 });
}
this->addChild(versionLabel);
this->addChild(m_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()
m_tagLabel = TagNode::create(tag->toString());
m_tagLabel->setAnchorPoint({.0f, .5f});
m_tagLabel->setScale(.2f);
m_tagLabel->setPosition(
m_versionLabel->getPositionX() + m_versionLabel->getScaledContentSize().width + 3.f,
m_versionLabel->getPositionY()
);
this->addChild(tagLabel);
this->addChild(m_tagLabel);
}
}
@ -134,7 +148,7 @@ bool ModInstallListCell::init(Mod* mod, InstallListPopup* list, CCSize const& si
this->setupInfo(mod->getMetadata(), true);
auto message = CCLabelBMFont::create("Installed", "bigFont.fnt");
message->setAnchorPoint({ 1.f, .5f });
message->setPositionX(m_menu->getPositionX());
message->setPositionX(m_width - 10.0f);
message->setPositionY(16.f);
message->setScale(0.4f);
message->setColor({ 163, 163, 163 });
@ -174,23 +188,38 @@ bool IndexItemInstallListCell::init(
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;
}
// TODO: show installed label properly
// if (item->isInstalled()) {
// auto message = CCLabelBMFont::create("Installed", "bigFont.fnt");
// message->setAnchorPoint({ 1.f, .5f });
// message->setPositionX(m_width - 10.0f);
// 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);
m_toggle->setAnchorPoint({1.f, .5f});
m_toggle->setPosition(m_width - 5, m_height / 2);
// recycling sprites in my Geode?? noo never
auto versionSelectSpr = EditorButtonSprite::createWithSpriteFrameName(
"filters.png"_spr, 1.0f, EditorBaseColor::Gray
);
versionSelectSpr->setScale(.7f);
auto versionSelectBtn =
CCMenuItemSpriteExtra::create(versionSelectSpr, this, menu_selector(IndexItemInstallListCell::onSelectVersion));
versionSelectBtn->setAnchorPoint({1.f, .5f});
versionSelectBtn->setPosition({m_toggle->getPositionX() - m_toggle->getContentSize().width - 5, m_height / 2});
m_menu->addChild(versionSelectBtn);
switch (importance) {
case ModMetadata::Dependency::Importance::Required:
@ -207,13 +236,18 @@ bool IndexItemInstallListCell::init(
break;
}
if (item->isInstalled()) {
m_toggle->setClickable(false);
m_toggle->toggle(true);
}
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->setPositionX(m_width - 5.f);
message->setPositionY(16.f);
message->setScale(0.4f);
message->setColor({ 240, 31, 31 });
@ -269,6 +303,15 @@ IndexItemHandle IndexItemInstallListCell::getItem() {
return m_item;
}
void IndexItemInstallListCell::setVersionFromItem(IndexItemHandle item) {
this->setupVersion(item->getMetadata().getVersion());
m_item = item;
}
void IndexItemInstallListCell::onSelectVersion(CCObject*) {
SelectVersionPopup::create(m_item->getMetadata().getID(), this)->show();
}
// UnknownInstallListCell
bool UnknownInstallListCell::init(
@ -317,3 +360,50 @@ std::string UnknownInstallListCell::getID() const {
std::string UnknownInstallListCell::getDeveloper() const {
return "";
}
// SelectVersionCell
bool SelectVersionCell::init(IndexItemHandle item, SelectVersionPopup* versionPopup, CCSize const& size) {
if (!InstallListCell::init(nullptr, size))
return false;
m_item = item;
m_versionPopup = versionPopup;
this->setupInfo(item->getMetadata(), item->isInstalled());
auto selectSpr = ButtonSprite::create(
"Select", 0, 0, "bigFont.fnt", "GJ_button_01.png", 0, .6f
);
selectSpr->setScale(.6f);
auto selectBtn = CCMenuItemSpriteExtra::create(
selectSpr, this, menu_selector(SelectVersionCell::onSelected)
);
selectBtn->setAnchorPoint({1.f, .5f});
selectBtn->setPosition({m_width - 5, m_height / 2});
m_menu->addChild(selectBtn);
return true;
}
void SelectVersionCell::onSelected(CCObject*) {
m_versionPopup->selectItem(m_item);
}
SelectVersionCell* SelectVersionCell::create(IndexItemHandle item, SelectVersionPopup* versionPopup, CCSize const& size) {
auto ret = new SelectVersionCell();
if (ret->init(item, versionPopup, size)) {
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
CCNode* SelectVersionCell::createLogo(CCSize const& size) {
return geode::createIndexItemLogo(m_item, size);
}
std::string SelectVersionCell::getID() const {
return m_item->getMetadata().getID();
}
std::string SelectVersionCell::getDeveloper() const {
return m_item->getMetadata().getDeveloper();
}

View file

@ -6,6 +6,8 @@
#include <Geode/loader/ModMetadata.hpp>
#include <Geode/loader/Index.hpp>
#include "../info/TagNode.hpp"
using namespace geode::prelude;
class InstallListPopup;
@ -17,10 +19,14 @@ class InstallListCell : public CCLayer {
protected:
float m_width;
float m_height;
InstallListPopup* m_layer;
CCMenu* m_menu;
CCMenuItemSpriteExtra* m_developerBtn;
InstallListPopup* m_layer = nullptr;
CCMenu* m_menu = nullptr;
CCMenuItemSpriteExtra* m_developerBtn = nullptr;
CCLabelBMFont* m_titleLabel = nullptr;
CCLabelBMFont* m_versionLabel = nullptr;
TagNode* m_tagLabel = nullptr;
CCMenuItemToggler* m_toggle = nullptr;
bool m_inactive = false;
void setupInfo(
std::string name,
@ -29,6 +35,8 @@ protected:
bool inactive
);
void setupVersion(std::variant<VersionInfo, ComparableVersionInfo> version);
bool init(InstallListPopup* list, CCSize const& size);
void setupInfo(ModMetadata const& metadata, bool inactive);
void draw() override;
@ -90,6 +98,9 @@ public:
[[nodiscard]] std::string getDeveloper() const override;
IndexItemHandle getItem();
void setVersionFromItem(IndexItemHandle item);
void onSelectVersion(CCObject*);
};
/**
@ -112,3 +123,23 @@ public:
[[nodiscard]] std::string getID() const override;
[[nodiscard]] std::string getDeveloper() const override;
};
class SelectVersionPopup;
/**
* Select version list item
*/
class SelectVersionCell : public InstallListCell {
protected:
IndexItemHandle m_item;
SelectVersionPopup* m_versionPopup;
bool init(IndexItemHandle item, SelectVersionPopup* versionPopup, CCSize const& size);
void onSelected(CCObject*);
public:
static SelectVersionCell* create(IndexItemHandle item, SelectVersionPopup* versionPopup, CCSize const& size);
CCNode* createLogo(CCSize const& size) override;
[[nodiscard]] std::string getID() const override;
[[nodiscard]] std::string getDeveloper() const override;
};

View file

@ -112,7 +112,8 @@ CCArray* InstallListPopup::createCells(std::unordered_map<std::string, InstallLi
queued.insert(item.id);
// installed
if (item.mod && !item.mod->isUninstalled()) {
// TODO: we should be able to select a different version even if its installed
if (/*item.mod && !item.mod->isUninstalled()*/item.mod->getMetadata().getID() == "geode.loader") {
bottom.push_back(ModInstallListCell::create(item.mod, this, this->getCellSize()));
for (auto const& dep : item.mod->getMetadata().getDependencies()) {
queue.push(dep);
@ -204,7 +205,7 @@ void InstallListPopup::onInstall(cocos2d::CCObject* obj) {
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())
if (!itemCell || !itemCell->isIncluded() || itemCell->getItem()->isInstalled())
continue;
IndexItemHandle item = itemCell->getItem();
list.list.push_back(item);
@ -227,3 +228,70 @@ InstallListPopup* InstallListPopup::create(
ret->autorelease();
return ret;
}
// SelectVersionPopup
bool SelectVersionPopup::setup(std::string const& modID, IndexItemInstallListCell* installCell) {
m_modID = modID;
m_installCell = installCell;
this->setTitle("Select Version");
this->createList();
return true;
}
void SelectVersionPopup::createList() {
auto winSize = CCDirector::sharedDirector()->getWinSize();
if (m_listParent) {
m_listParent->removeFromParent();
}
m_listParent = CCNode::create();
m_mainLayer->addChild(m_listParent);
auto items = this->createCells();
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);
addListBorders(m_listParent, winSize / 2, m_list->getScaledContentSize());
}
CCArray* SelectVersionPopup::createCells() {
auto cells = CCArray::create();
for (auto& item : ranges::reverse(Index::get()->getItemsByModID(m_modID))) {
cells->addObject(SelectVersionCell::create(item, this, this->getCellSize()));
}
return cells;
}
CCSize SelectVersionPopup::getCellSize() const {
return { getListSize().width, 30.f };
}
CCSize SelectVersionPopup::getListSize() const {
return { 340.f, 170.f };
}
void SelectVersionPopup::selectItem(IndexItemHandle item) {
this->onBtn2(nullptr);
m_installCell->setVersionFromItem(item);
}
SelectVersionPopup* SelectVersionPopup::create(std::string const& modID, IndexItemInstallListCell* installCell) {
auto ret = new SelectVersionPopup();
if (!ret->init(380.f, 250.f, modID, installCell)) {
CC_SAFE_DELETE(ret);
return nullptr;
}
ret->autorelease();
return ret;
}

View file

@ -28,3 +28,24 @@ public:
static InstallListPopup* create(IndexItemHandle item, MiniFunction<void(IndexInstallList const&)> onInstall);
};
class SelectVersionPopup : public Popup<std::string const&, IndexItemInstallListCell*> {
protected:
std::string m_modID;
CCNode* m_listParent;
ListView* m_list;
IndexItemInstallListCell* m_installCell;
bool setup(std::string const& modID, IndexItemInstallListCell* installCell) override;
void createList();
CCArray* createCells();
CCSize getCellSize() const;
CCSize getListSize() const;
public:
void selectItem(IndexItemHandle item);
static SelectVersionPopup* create(std::string const& modID, IndexItemInstallListCell* installCell);
};

View file

@ -198,7 +198,7 @@ CCArray* ModListLayer::createModCells(ModListType type, ModListQuery const& quer
std::multimap<int, IndexItemHandle> sorted;
auto index = Index::get();
for (auto const& item : index->getItems()) {
for (auto const& item : index->getLatestItems()) {
if (auto match = queryMatch(query, item)) {
sorted.insert({ match.value(), item });
}