mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-27 09:55:34 -05:00
Merge branch 'geode-sdk:main' into copy-mods
This commit is contained in:
commit
a68a631a4d
52 changed files with 534 additions and 210 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,6 +1,15 @@
|
|||
# Geode Changelog
|
||||
|
||||
## v3.7.2
|
||||
## v3.8.1
|
||||
* Fix CCLightning header
|
||||
* Fix server query default value (8be97b7)
|
||||
* Fix importance resolving in disabled mods (d40ba6d)
|
||||
|
||||
## v3.8.0
|
||||
* Add Modtober integration! For more about Modtober, join [the GDP Discord server](https://discord.gg/gd-programming-646101505417674758) (964624b)
|
||||
* Add `Popup::CloseEvent` (6270e1c)
|
||||
* Add `openSettingsPopup` overload that returns the created `Popup` (dc8d271)
|
||||
* Fix `CCNode::querySelector` logspamming (b53759f)
|
||||
* Fix `followThunkFunction` following through into hook handlers (ad26357)
|
||||
|
||||
## v3.7.1
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
3.7.2
|
||||
3.8.1
|
||||
|
|
|
@ -450,7 +450,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
__node_alloc_traits::_S_propagate_on_move_assign()
|
||||
|| __node_alloc_traits::_S_always_equal();
|
||||
_M_move_assign(std::move(__ht),
|
||||
integral_constant<bool, __move_storage>());
|
||||
std::integral_constant<bool, __move_storage>());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1115,7 +1115,7 @@ public:
|
|||
geode::utils::MiniFunction<typename Filter::Callback> callback,
|
||||
Args&&... args
|
||||
) {
|
||||
return this->template addEventListener<Filter, Args...>(
|
||||
return this->addEventListener<Filter, Args...>(
|
||||
"", callback, std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
|
|
@ -181,6 +181,7 @@ public:
|
|||
* @lua NA
|
||||
*/
|
||||
CCDictionary();
|
||||
GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCDictionary, CCObject);
|
||||
|
||||
/**
|
||||
* The destructor of CCDictionary
|
||||
|
|
|
@ -11,7 +11,7 @@ class CCLightning : public CCNode, public CCRGBAProtocol {
|
|||
public:
|
||||
CCLightning();
|
||||
virtual ~CCLightning();
|
||||
GEODE_CUSTOM_CONSTRUCTOR_GD(CCLightning, CCNode);
|
||||
GEODE_CUTOFF_CONSTRUCTOR_GD(CCLightning, CCNode);
|
||||
|
||||
static CCLightning* lightningWithStrikePoint(CCPoint strikePoint, CCPoint strikePoint2, float duration);
|
||||
static CCLightning* lightningWithStrikePoint(CCPoint strikePoint);
|
||||
|
|
|
@ -324,8 +324,8 @@ namespace geode {
|
|||
}
|
||||
|
||||
bool load(matjson::Value const& json) override {
|
||||
if (json.template is<T>()) {
|
||||
m_impl->value = json.template as<T>();
|
||||
if (json.is<T>()) {
|
||||
m_impl->value = json.as<T>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -86,7 +86,7 @@ namespace geode {
|
|||
public:
|
||||
static ListBorders* create();
|
||||
|
||||
void setSpriteFrames(const char* topAndBottom, const char* sides, float topPadding = 7.5f);
|
||||
void setSpriteFrames(const char* topAndBottom, const char* sides, float horizontalPadding = 7.5f);
|
||||
void setSprites(
|
||||
cocos2d::extension::CCScale9Sprite* top,
|
||||
cocos2d::extension::CCScale9Sprite* bottom,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "../loader/Mod.hpp"
|
||||
#include <Geode/binding/FLAlertLayer.hpp>
|
||||
#include <Geode/ui/Popup.hpp>
|
||||
|
||||
class ModPopup;
|
||||
class ModItem;
|
||||
|
@ -141,6 +142,16 @@ namespace geode {
|
|||
* Open the settings popup for a mod (if it has any settings)
|
||||
*/
|
||||
GEODE_DLL void openSettingsPopup(Mod* mod);
|
||||
/**
|
||||
* Open the settings popup for a mod (if it has any settings)
|
||||
* @param mod Mod the open the popup for
|
||||
* @param disableGeodeTheme If false, the popup follows the user's chosen
|
||||
* theme options. If true, the popup is always in the GD theme (not Geode's
|
||||
* dark purple colors)
|
||||
* @returns A pointer to the created Popup, or null if the mod has no
|
||||
* settings
|
||||
*/
|
||||
GEODE_DLL Popup<Mod*>* openSettingsPopup(Mod* mod, bool disableGeodeTheme);
|
||||
/**
|
||||
* Create a default logo sprite
|
||||
*/
|
||||
|
|
|
@ -6,8 +6,59 @@
|
|||
#include <Geode/utils/cocos.hpp>
|
||||
|
||||
namespace geode {
|
||||
template <typename... InitArgs>
|
||||
template <class... InitArgs>
|
||||
class Popup : public FLAlertLayer {
|
||||
public:
|
||||
/**
|
||||
* Event posted when this popup is being closed
|
||||
*/
|
||||
class CloseEvent final : public ::geode::Event {
|
||||
private:
|
||||
class Impl final {
|
||||
private:
|
||||
Popup* popup;
|
||||
friend class CloseEvent;
|
||||
};
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
friend class Popup;
|
||||
|
||||
CloseEvent(Popup* popup) : m_impl(std::make_shared<Impl>()) {
|
||||
m_impl->popup = popup;
|
||||
}
|
||||
|
||||
public:
|
||||
Popup* getPopup() const {
|
||||
return m_impl->popup;
|
||||
}
|
||||
};
|
||||
class CloseEventFilter final : public ::geode::EventFilter<CloseEvent> {
|
||||
public:
|
||||
using Callback = void(CloseEvent*);
|
||||
|
||||
private:
|
||||
class Impl final {
|
||||
private:
|
||||
Popup* popup;
|
||||
friend class CloseEventFilter;
|
||||
};
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
friend class Popup;
|
||||
|
||||
CloseEventFilter(Popup* popup) : m_impl(std::make_shared<Impl>()) {
|
||||
m_impl->popup = popup;
|
||||
}
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, CloseEvent* event) {
|
||||
if (event->getPopup() == m_impl->popup) {
|
||||
fn(event);
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
cocos2d::CCSize m_size;
|
||||
cocos2d::extension::CCScale9Sprite* m_bgSprite;
|
||||
|
@ -115,6 +166,7 @@ namespace geode {
|
|||
}
|
||||
|
||||
virtual void onClose(cocos2d::CCObject*) {
|
||||
CloseEvent(this).post();
|
||||
this->setKeypadEnabled(false);
|
||||
this->setTouchEnabled(false);
|
||||
this->removeFromParentAndCleanup(true);
|
||||
|
@ -158,6 +210,13 @@ namespace geode {
|
|||
spr->setAnchorPoint(orig->getAnchorPoint());
|
||||
m_closeBtn->setContentSize(origSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an event filter that listens for when this popup is closed
|
||||
*/
|
||||
CloseEventFilter listenForClose() {
|
||||
return CloseEventFilter(this);
|
||||
}
|
||||
};
|
||||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <set>
|
||||
#include <variant>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
|
||||
namespace geode {
|
||||
struct JsonChecker;
|
||||
|
@ -147,14 +148,14 @@ namespace geode {
|
|||
template <class T>
|
||||
bool is() {
|
||||
if (this->isError()) return false;
|
||||
return self().m_json.template is<T>();
|
||||
return self().m_json.is<T>();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
JsonMaybeValue& validate(JsonValueValidator<T> validator) {
|
||||
if (this->isError()) return *this;
|
||||
if (self().m_json.template is<T>()) {
|
||||
if (!validator(self().m_json.template as<T>())) {
|
||||
if (self().m_json.is<T>()) {
|
||||
if (!validator(self().m_json.as<T>())) {
|
||||
this->setError(self().m_hierarchy + ": Invalid value format");
|
||||
}
|
||||
}
|
||||
|
@ -195,9 +196,9 @@ namespace geode {
|
|||
this->inferType<A>();
|
||||
if (this->isError()) return *this;
|
||||
|
||||
if (self().m_json.template is<A>()) {
|
||||
if (self().m_json.is<A>()) {
|
||||
try {
|
||||
target = self().m_json.template as<A>();
|
||||
target = self().m_json.as<A>();
|
||||
}
|
||||
catch(matjson::JsonException const& e) {
|
||||
this->setError(
|
||||
|
@ -219,8 +220,8 @@ namespace geode {
|
|||
T get() {
|
||||
this->inferType<T>();
|
||||
if (this->isError()) return T();
|
||||
if (self().m_json.template is<T>()) {
|
||||
return self().m_json.template as<T>();
|
||||
if (self().m_json.is<T>()) {
|
||||
return self().m_json.as<T>();
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
@ -325,8 +326,8 @@ namespace geode {
|
|||
}
|
||||
else {
|
||||
try {
|
||||
if (this->getJSONRef().template is<T>()) {
|
||||
return this->getJSONRef().template as<T>();
|
||||
if (this->getJSONRef().is<T>()) {
|
||||
return this->getJSONRef().as<T>();
|
||||
}
|
||||
else {
|
||||
this->setError(
|
||||
|
@ -396,21 +397,21 @@ namespace geode {
|
|||
|
||||
template <class T>
|
||||
T get(T const& defaultValue = T()) {
|
||||
if (auto v = this->template tryGet<T>()) {
|
||||
if (auto v = this->tryGet<T>()) {
|
||||
return *std::move(v);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
template <class T>
|
||||
JsonExpectedValue& into(T& value) {
|
||||
if (auto v = this->template tryGet<T>()) {
|
||||
if (auto v = this->tryGet<T>()) {
|
||||
value = *std::move(v);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <class T>
|
||||
JsonExpectedValue& into(std::optional<T>& value) {
|
||||
if (auto v = this->template tryGet<T>()) {
|
||||
if (auto v = this->tryGet<T>()) {
|
||||
value.emplace(*std::move(v));
|
||||
}
|
||||
return *this;
|
||||
|
@ -420,7 +421,7 @@ namespace geode {
|
|||
{ predicate(std::declval<T>()) } -> std::convertible_to<bool>;
|
||||
} {
|
||||
if (this->hasError()) return *this;
|
||||
if (auto v = this->template tryGet<T>()) {
|
||||
if (auto v = this->tryGet<T>()) {
|
||||
if (!predicate(*v)) {
|
||||
this->setError("json value is not {}", name);
|
||||
}
|
||||
|
@ -432,7 +433,7 @@ namespace geode {
|
|||
{ predicate(std::declval<T>()) } -> std::convertible_to<Result<>>;
|
||||
} {
|
||||
if (this->hasError()) return *this;
|
||||
if (auto v = this->template tryGet<T>()) {
|
||||
if (auto v = this->tryGet<T>()) {
|
||||
auto p = predicate(*v);
|
||||
if (!p) {
|
||||
this->setError("json value is not {}: {}", name, p.unwrapErr());
|
||||
|
|
|
@ -32,10 +32,10 @@ namespace geode::utils::file {
|
|||
template <class T>
|
||||
Result<T> readFromJson(std::filesystem::path const& file) {
|
||||
GEODE_UNWRAP_INTO(auto json, readJson(file));
|
||||
if (!json.template is<T>()) {
|
||||
if (!json.is<T>()) {
|
||||
return Err("JSON is not of type {}", typeid(T).name());
|
||||
}
|
||||
return Ok(json.template as<T>());
|
||||
return Ok(json.as<T>());
|
||||
}
|
||||
|
||||
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);
|
||||
|
|
|
@ -137,8 +137,9 @@ namespace geode {
|
|||
if constexpr (std::is_floating_point_v<Num>) res = std::from_chars(str.data(), str.data() + str.size(), result);
|
||||
else res = std::from_chars(str.data(), str.data() + str.size(), result, base);
|
||||
|
||||
auto [_, ec] = res;
|
||||
auto [ptr, ec] = res;
|
||||
if (ec == std::errc()) return Ok(result);
|
||||
else if (ptr != str.data() + str.size()) return Err("String contains trailing extra data");
|
||||
else if (ec == std::errc::invalid_argument) return Err("String is not a number");
|
||||
else if (ec == std::errc::result_out_of_range) return Err("Number is too large to fit");
|
||||
else return Err("Unknown error");
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <compare>
|
||||
|
||||
namespace geode::utils::string {
|
||||
/**
|
||||
|
@ -64,4 +64,10 @@ namespace geode::utils::string {
|
|||
|
||||
GEODE_DLL bool startsWith(std::string const& str, std::string const& prefix);
|
||||
GEODE_DLL bool endsWith(std::string const& str, std::string const& suffix);
|
||||
|
||||
/**
|
||||
* Similar to strcmp, but case insensitive.
|
||||
* Uses std::tolower, but could change in the future for better locale support
|
||||
*/
|
||||
GEODE_DLL std::strong_ordering caseInsensitiveCompare(std::string_view a, std::string_view b);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,9 @@
|
|||
],
|
||||
"BlankSheet": [
|
||||
"blanks/*.png"
|
||||
],
|
||||
"EventSheet": [
|
||||
"modtober/*.png"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
BIN
loader/resources/modtober/modtober24-banner-2.png
Normal file
BIN
loader/resources/modtober/modtober24-banner-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 101 KiB |
BIN
loader/resources/modtober/modtober24-banner.png
Normal file
BIN
loader/resources/modtober/modtober24-banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
BIN
loader/resources/modtober/modtober24-popup.png
Normal file
BIN
loader/resources/modtober/modtober24-popup.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 MiB |
BIN
loader/resources/tag-modtober.png
Normal file
BIN
loader/resources/tag-modtober.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
|
@ -144,7 +144,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
|||
if (
|
||||
crashlog::didLastLaunchCrash() &&
|
||||
!shownLastCrash &&
|
||||
!Mod::get()->template getSettingValue<bool>("disable-last-crashed-popup")
|
||||
!Mod::get()->getSettingValue<bool>("disable-last-crashed-popup")
|
||||
) {
|
||||
shownLastCrash = true;
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ $register_ids(MenuLayer) {
|
|||
// but prolly a place mods want to add stuff
|
||||
|
||||
auto topRightMenu = CCMenu::create();
|
||||
topRightMenu->setPosition(winSize.width - 200.f / 2, winSize.height - 50.f / 2);
|
||||
topRightMenu->setPosition(winSize.width - 210.f / 2, winSize.height - 50.f / 2);
|
||||
topRightMenu->setID("top-right-menu");
|
||||
topRightMenu->setContentSize({ 200.f, 50.f });
|
||||
topRightMenu->setLayout(
|
||||
|
|
|
@ -33,8 +33,8 @@ $on_mod(Loaded) {
|
|||
JsonChecker checker(args);
|
||||
auto root = checker.root("[ipc/list-mods]").obj();
|
||||
|
||||
auto includeRunTimeInfo = root.has("include-runtime-info").template get<bool>();
|
||||
auto dontIncludeLoader = root.has("dont-include-loader").template get<bool>();
|
||||
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
|
||||
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();
|
||||
|
||||
if (!dontIncludeLoader) {
|
||||
res.push_back(
|
||||
|
|
|
@ -535,7 +535,7 @@ void Loader::Impl::findProblems() {
|
|||
|
||||
switch(dep.importance) {
|
||||
case ModMetadata::Dependency::Importance::Suggested:
|
||||
if (!Mod::get()->template getSavedValue<bool>(dismissKey)) {
|
||||
if (!Mod::get()->getSavedValue<bool>(dismissKey)) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::Suggestion,
|
||||
mod,
|
||||
|
@ -548,7 +548,7 @@ void Loader::Impl::findProblems() {
|
|||
}
|
||||
break;
|
||||
case ModMetadata::Dependency::Importance::Recommended:
|
||||
if (!Mod::get()->template getSavedValue<bool>(dismissKey)) {
|
||||
if (!Mod::get()->getSavedValue<bool>(dismissKey)) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::Recommendation,
|
||||
mod,
|
||||
|
|
|
@ -57,7 +57,7 @@ bool ModMetadata::Dependency::isResolved() const {
|
|||
|
||||
bool ModMetadata::Incompatibility::isResolved() const {
|
||||
return this->importance != Importance::Breaking ||
|
||||
(!this->mod || !this->version.compare(this->mod->getVersion()));
|
||||
(!this->mod || !this->mod->isEnabled() || !this->version.compare(this->mod->getVersion()));
|
||||
}
|
||||
|
||||
static std::string sanitizeDetailsData(std::string const& str) {
|
||||
|
@ -170,7 +170,7 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
|
|||
return Err("[mod.json] can not have both \"developer\" and \"developers\" specified");
|
||||
}
|
||||
for (auto& dev : root.needs("developers").items()) {
|
||||
impl->m_developers.push_back(dev.template get<std::string>());
|
||||
impl->m_developers.push_back(dev.get<std::string>());
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -290,7 +290,7 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
|
|||
|
||||
// Tags. Actual validation is done when interacting with the server in the UI
|
||||
for (auto& tag : root.has("tags").items()) {
|
||||
impl->m_tags.insert(tag.template get<std::string>());
|
||||
impl->m_tags.insert(tag.get<std::string>());
|
||||
}
|
||||
|
||||
// with new cli, binary name is always mod id
|
||||
|
|
|
@ -16,7 +16,7 @@ static void parseCommon(T& sett, JsonMaybeObject& obj) {
|
|||
obj.has("description").into(sett.description);
|
||||
if (auto defValue = obj.needs("default")) {
|
||||
// Platform-specific default value
|
||||
if (defValue.template is<matjson::Object>()) {
|
||||
if (defValue.is<matjson::Object>()) {
|
||||
auto def = defValue.obj();
|
||||
if (auto plat = def.has(PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))) {
|
||||
plat.into(sett.defaultValue);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "SettingNodeV3.hpp"
|
||||
#include <Geode/loader/SettingNode.hpp>
|
||||
#include <Geode/utils/ColorProvider.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <ui/mods/GeodeStyle.hpp>
|
||||
|
||||
class SettingNodeSizeChangeEventV3::Impl final {
|
||||
|
@ -349,6 +351,10 @@ bool StringSettingNodeV3::init(std::shared_ptr<StringSettingV3> setting, float w
|
|||
});
|
||||
m_input->setScale(.7f);
|
||||
m_input->setString(this->getSetting()->getValue());
|
||||
if (auto filter = this->getSetting()->getAllowedCharacters()) {
|
||||
m_input->setFilter(*filter);
|
||||
}
|
||||
|
||||
this->getButtonMenu()->addChildAtPosition(m_input, Anchor::Center);
|
||||
|
||||
if (setting->getEnumOptions()) {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/loader/SettingNode.hpp>
|
||||
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||
#include <Geode/binding/ColorChannelSprite.hpp>
|
||||
#include <Geode/binding/Slider.hpp>
|
||||
#include <Geode/ui/ColorPickPopup.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <Geode/loader/ModSettingsManager.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <regex>
|
||||
#include "SettingNodeV3.hpp"
|
||||
|
@ -51,7 +54,7 @@ namespace enable_if_parsing {
|
|||
}
|
||||
Result<> eval(std::string const& defaultModID) const override {
|
||||
if (auto mod = Loader::get()->getLoadedMod(modID)) {
|
||||
if (mod->template getSettingValue<bool>(settingID)) {
|
||||
if (mod->getSettingValue<bool>(settingID)) {
|
||||
return Ok();
|
||||
}
|
||||
// This is an if-check just in case, even though check() should already
|
||||
|
@ -83,7 +86,7 @@ namespace enable_if_parsing {
|
|||
}
|
||||
Result<> eval(std::string const& defaultModID) const override {
|
||||
if (auto mod = Loader::get()->getLoadedMod(modID)) {
|
||||
if (mod->template getSavedValue<bool>(savedValue)) {
|
||||
if (mod->getSavedValue<bool>(savedValue)) {
|
||||
return Ok();
|
||||
}
|
||||
if (modID == defaultModID) {
|
||||
|
@ -482,7 +485,7 @@ void SettingV3::init(std::string const& key, std::string const& modID, JsonExpec
|
|||
// Keys every setting must have
|
||||
json.needs("type");
|
||||
for (auto& plat : json.has("platforms").items()) {
|
||||
ranges::push(m_impl->platforms, PlatformID::getCovered(plat.template get<std::string>()));
|
||||
ranges::push(m_impl->platforms, PlatformID::getCovered(plat.get<std::string>()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,7 +495,7 @@ void SettingV3::parseNameAndDescription(JsonExpectedValue& json) {
|
|||
}
|
||||
void SettingV3::parseEnableIf(JsonExpectedValue& json) {
|
||||
json.has("enable-if")
|
||||
.template mustBe<std::string>("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> {
|
||||
.mustBe<std::string>("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> {
|
||||
GEODE_UNWRAP_INTO(auto tree, enable_if_parsing::Parser::parse(str, m_impl->modID));
|
||||
GEODE_UNWRAP(tree->check());
|
||||
m_impl->enableIfTree = std::move(tree);
|
||||
|
@ -745,10 +748,10 @@ Result<std::shared_ptr<IntSettingV3>> IntSettingV3::parse(std::string const& key
|
|||
// This silly code is because step size being 0 is what defines if they are enabled
|
||||
|
||||
// Small arrows are enabled by default
|
||||
if (!root.has("control").has("arrows").template get<bool>(true)) {
|
||||
if (!root.has("control").has("arrows").get<bool>(true)) {
|
||||
ret->m_impl->controls.arrowStepSize = 0;
|
||||
}
|
||||
if (!root.has("control").has("big-arrows").template get<bool>()) {
|
||||
if (!root.has("control").has("big-arrows").get<bool>()) {
|
||||
ret->m_impl->controls.bigArrowStepSize = 0;
|
||||
}
|
||||
|
||||
|
@ -876,10 +879,10 @@ Result<std::shared_ptr<FloatSettingV3>> FloatSettingV3::parse(std::string const&
|
|||
|
||||
// Disable arrows if they aren't enabled
|
||||
// Small arrows are enabled by default
|
||||
if (!root.has("control").has("arrows").template get<bool>(true)) {
|
||||
if (!root.has("control").has("arrows").get<bool>(true)) {
|
||||
ret->m_impl->controls.arrowStepSize = 0;
|
||||
}
|
||||
if (!root.has("control").has("big-arrows").template get<bool>()) {
|
||||
if (!root.has("control").has("big-arrows").get<bool>()) {
|
||||
ret->m_impl->controls.bigArrowStepSize = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,9 +109,9 @@ void updater::downloadLatestLoaderResources() {
|
|||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||
if (obj.needs("name").get<std::string>() == "resources.zip") {
|
||||
updater::tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").template get<std::string>(),
|
||||
obj.needs("browser_download_url").get<std::string>(),
|
||||
false
|
||||
);
|
||||
return;
|
||||
|
@ -213,9 +213,9 @@ void updater::downloadLoaderResources(bool useLatestRelease) {
|
|||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||
if (obj.needs("name").get<std::string>() == "resources.zip") {
|
||||
updater::tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").template get<std::string>(),
|
||||
obj.needs("browser_download_url").get<std::string>(),
|
||||
false
|
||||
);
|
||||
return *response;
|
||||
|
@ -391,11 +391,11 @@ void updater::checkForLoaderUpdates() {
|
|||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (string::endsWith(
|
||||
obj.needs("name").template get<std::string>(),
|
||||
obj.needs("name").get<std::string>(),
|
||||
fmt::format("{}.zip", PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))
|
||||
)) {
|
||||
updater::downloadLoaderUpdate(
|
||||
obj.needs("browser_download_url").template get<std::string>()
|
||||
obj.needs("browser_download_url").get<std::string>()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -115,19 +115,19 @@ void console::setup() {
|
|||
path = std::string(buf, count - 1);
|
||||
}
|
||||
|
||||
// count == 0 => not a console and not a file, assume it's closed
|
||||
// wine does something weird with /dev/null? not sure tbh but it's definitely up to no good
|
||||
// TODO: the isWine check is pretty hacky but without it the game does not launch at all and i cba to figure it out rn
|
||||
if ((count == 0 || path.ends_with("\\dev\\null"))) {
|
||||
s_outHandle = nullptr;
|
||||
CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
|
||||
CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
|
||||
FreeConsole();
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, nullptr);
|
||||
SetStdHandle(STD_INPUT_HANDLE, nullptr);
|
||||
SetStdHandle(STD_ERROR_HANDLE, nullptr);
|
||||
}
|
||||
// TODO: this code causes a crash when piping game's output somewhere (and in some other cases), so it's removed for now
|
||||
// // count == 0 => not a console and not a file, assume it's closed
|
||||
// // wine does something weird with /dev/null? not sure tbh but it's definitely up to no good
|
||||
// if ((count == 0 || path.ends_with("\\dev\\null"))) {
|
||||
// s_outHandle = nullptr;
|
||||
// CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
// CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
|
||||
// CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
|
||||
// FreeConsole();
|
||||
// SetStdHandle(STD_OUTPUT_HANDLE, nullptr);
|
||||
// SetStdHandle(STD_INPUT_HANDLE, nullptr);
|
||||
// SetStdHandle(STD_ERROR_HANDLE, nullptr);
|
||||
// }
|
||||
}
|
||||
|
||||
// clion console supports escape codes but we can't query that because it's a named pipe
|
||||
|
|
|
@ -189,7 +189,7 @@ static ServerError parseServerError(web::WebResponse const& error) {
|
|||
if (json.is_object() && json.contains("error")) {
|
||||
return ServerError(
|
||||
error.code(),
|
||||
"{}", json.template get<std::string>("error")
|
||||
"{}", json.get<std::string>("error")
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -264,13 +264,13 @@ Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
|
|||
|
||||
auto res = ServerModVersion();
|
||||
|
||||
res.metadata.setGeodeVersion(root.needs("geode").template get<VersionInfo>());
|
||||
res.metadata.setGeodeVersion(root.needs("geode").get<VersionInfo>());
|
||||
|
||||
// Verify target GD version
|
||||
auto gd_obj = root.needs("gd").obj();
|
||||
std::string gd = "0.000";
|
||||
if (gd_obj.has(GEODE_PLATFORM_SHORT_IDENTIFIER)) {
|
||||
gd = gd_obj.has(GEODE_PLATFORM_SHORT_IDENTIFIER).template get<std::string>();
|
||||
gd = gd_obj.has(GEODE_PLATFORM_SHORT_IDENTIFIER). get<std::string>();
|
||||
}
|
||||
|
||||
if (gd != "*") {
|
||||
|
@ -283,11 +283,11 @@ Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
|
|||
root.needs("hash").into(res.hash);
|
||||
|
||||
// Get mod metadata info
|
||||
res.metadata.setID(root.needs("mod_id").template get<std::string>());
|
||||
res.metadata.setName(root.needs("name").template get<std::string>());
|
||||
res.metadata.setDescription(root.needs("description").template get<std::string>());
|
||||
res.metadata.setVersion(root.needs("version").template get<VersionInfo>());
|
||||
res.metadata.setIsAPI(root.needs("api").template get<bool>());
|
||||
res.metadata.setID(root.needs("mod_id").get<std::string>());
|
||||
res.metadata.setName(root.needs("name").get<std::string>());
|
||||
res.metadata.setDescription(root.needs("description").get<std::string>());
|
||||
res.metadata.setVersion(root.needs("version").get<VersionInfo>());
|
||||
res.metadata.setIsAPI(root.needs("api").get<bool>());
|
||||
|
||||
std::vector<ModMetadata::Dependency> dependencies {};
|
||||
for (auto dep : root.has("dependencies").iterate()) {
|
||||
|
@ -433,10 +433,10 @@ Result<ServerModMetadata> ServerModMetadata::parse(matjson::Value const& raw) {
|
|||
root.has("changelog").into(res.changelog);
|
||||
root.has("repository").into(res.repository);
|
||||
if (root.has("created_at")) {
|
||||
GEODE_UNWRAP_INTO(res.createdAt, ServerDateTime::parse(root.has("created_at").template get<std::string>()));
|
||||
GEODE_UNWRAP_INTO(res.createdAt, ServerDateTime::parse(root.has("created_at").get<std::string>()));
|
||||
}
|
||||
if (root.has("updated_at")) {
|
||||
GEODE_UNWRAP_INTO(res.updatedAt, ServerDateTime::parse(root.has("updated_at").template get<std::string>()));
|
||||
GEODE_UNWRAP_INTO(res.updatedAt, ServerDateTime::parse(root.has("updated_at").get<std::string>()));
|
||||
}
|
||||
|
||||
std::vector<std::string> developerNames;
|
||||
|
@ -470,7 +470,7 @@ Result<ServerModMetadata> ServerModMetadata::parse(matjson::Value const& raw) {
|
|||
}
|
||||
|
||||
for (auto item : root.has("tags").iterate()) {
|
||||
res.tags.insert(item.template get<std::string>());
|
||||
res.tags.insert(item.get<std::string>());
|
||||
}
|
||||
|
||||
root.needs("download_count").into(res.downloadCount);
|
||||
|
|
|
@ -159,9 +159,15 @@ void geode::openChangelogPopup(Mod* mod) {
|
|||
}
|
||||
|
||||
void geode::openSettingsPopup(Mod* mod) {
|
||||
openSettingsPopup(mod, true);
|
||||
}
|
||||
Popup<Mod*>* geode::openSettingsPopup(Mod* mod, bool disableGeodeTheme) {
|
||||
if (mod->hasSettings()) {
|
||||
ModSettingsPopup::create(mod)->show();
|
||||
auto popup = ModSettingsPopup::create(mod, disableGeodeTheme);
|
||||
popup->show();
|
||||
return popup;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class ModLogoSprite : public CCNode {
|
||||
|
|
|
@ -70,16 +70,21 @@ $on_mod(Loaded) {
|
|||
Loader::get()->queueInMainThread([updateColors = updateColors] {
|
||||
// this code is ran during static init, where settings aren't loaded yet, and getSettingValue will always return false.
|
||||
// because of that, we have to delay it until next frame.
|
||||
updateColors(Mod::get()->template getSettingValue<bool>("enable-geode-theme"));
|
||||
updateColors(Mod::get()->getSettingValue<bool>("enable-geode-theme"));
|
||||
});
|
||||
}
|
||||
|
||||
bool GeodeSquareSprite::init(CCSprite* top, bool* state) {
|
||||
if (!CCSprite::initWithFile(isGeodeTheme() ? "GE_button_05.png"_spr : "GJ_button_01.png"))
|
||||
bool isGeodeTheme(bool forceDisableTheme) {
|
||||
return !forceDisableTheme && Mod::get()->getSettingValue<bool>("enable-geode-theme");
|
||||
}
|
||||
|
||||
bool GeodeSquareSprite::init(CCSprite* top, bool* state, bool forceDisableTheme) {
|
||||
if (!CCSprite::initWithFile(isGeodeTheme(forceDisableTheme) ? "GE_button_05.png"_spr : "GJ_button_01.png"))
|
||||
return false;
|
||||
|
||||
m_stateSrc = state;
|
||||
m_topSprite = top;
|
||||
m_forceDisableTheme = forceDisableTheme;
|
||||
|
||||
limitNodeSize(top, m_obContentSize * .65f, 2.f, .1f);
|
||||
this->addChildAtPosition(top, Anchor::Center);
|
||||
|
@ -94,7 +99,7 @@ bool GeodeSquareSprite::init(CCSprite* top, bool* state) {
|
|||
|
||||
void GeodeSquareSprite::updateImage() {
|
||||
this->setTexture(CCTextureCache::get()->addImage(
|
||||
(m_state ? "GJ_button_02.png" : (isGeodeTheme() ? "GE_button_05.png"_spr : "GJ_button_01.png")),
|
||||
(m_state ? "GJ_button_02.png" : (isGeodeTheme(m_forceDisableTheme) ? "GE_button_05.png"_spr : "GJ_button_01.png")),
|
||||
false
|
||||
));
|
||||
}
|
||||
|
@ -106,18 +111,18 @@ void GeodeSquareSprite::update(float dt) {
|
|||
}
|
||||
}
|
||||
|
||||
GeodeSquareSprite* GeodeSquareSprite::create(const char* top, bool* state) {
|
||||
GeodeSquareSprite* GeodeSquareSprite::create(const char* top, bool* state, bool forceDisableTheme) {
|
||||
auto ret = new GeodeSquareSprite();
|
||||
if (ret->init(CCSprite::create(top), state)) {
|
||||
if (ret->init(CCSprite::create(top), state, forceDisableTheme)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
delete ret;
|
||||
return nullptr;
|
||||
}
|
||||
GeodeSquareSprite* GeodeSquareSprite::createWithSpriteFrameName(const char* top, bool* state) {
|
||||
GeodeSquareSprite* GeodeSquareSprite::createWithSpriteFrameName(const char* top, bool* state, bool forceDisableTheme) {
|
||||
auto ret = new GeodeSquareSprite();
|
||||
if (ret->init(CCSprite::createWithSpriteFrameName(top), state)) {
|
||||
if (ret->init(CCSprite::createWithSpriteFrameName(top), state, forceDisableTheme)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
|
@ -142,8 +147,8 @@ CCNode* createLoadingCircle(float sideLength, const char* id) {
|
|||
return spinner;
|
||||
}
|
||||
|
||||
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr) {
|
||||
if (isGeodeTheme()) {
|
||||
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr, bool forceDisableTheme) {
|
||||
if (isGeodeTheme(forceDisableTheme)) {
|
||||
switch (spr) {
|
||||
default:
|
||||
case GeodeButtonSprite::Default: return "GE_button_05.png"_spr;
|
||||
|
@ -165,18 +170,18 @@ const char* getGeodeButtonSpriteName(GeodeButtonSprite spr) {
|
|||
}
|
||||
}
|
||||
|
||||
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg) {
|
||||
return IconButtonSprite::create(getGeodeButtonSpriteName(bg), icon, text.c_str(), "bigFont.fnt");
|
||||
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg, bool forceDisableTheme) {
|
||||
return IconButtonSprite::create(getGeodeButtonSpriteName(bg, forceDisableTheme), icon, text.c_str(), "bigFont.fnt");
|
||||
}
|
||||
ButtonSprite* createGeodeButton(std::string const& text, int width, bool gold, bool absolute, GeodeButtonSprite bg) {
|
||||
return ButtonSprite::create(text.c_str(), width, absolute, gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg), 0.0f, .8f);
|
||||
ButtonSprite* createGeodeButton(std::string const& text, int width, bool gold, bool absolute, GeodeButtonSprite bg, bool forceDisableTheme) {
|
||||
return ButtonSprite::create(text.c_str(), width, absolute, gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg, forceDisableTheme), 0.0f, .8f);
|
||||
}
|
||||
ButtonSprite* createGeodeButton(std::string const& text, bool gold, GeodeButtonSprite bg) {
|
||||
return ButtonSprite::create(text.c_str(), gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg), .8f);
|
||||
ButtonSprite* createGeodeButton(std::string const& text, bool gold, GeodeButtonSprite bg, bool forceDisableTheme) {
|
||||
return ButtonSprite::create(text.c_str(), gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg, forceDisableTheme), .8f);
|
||||
}
|
||||
|
||||
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale, CircleBaseSize size, bool altColor) {
|
||||
const auto geodeTheme = isGeodeTheme();
|
||||
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale, CircleBaseSize size, bool altColor, bool forceDisableTheme) {
|
||||
const auto geodeTheme = isGeodeTheme(forceDisableTheme);
|
||||
auto ret = CircleButtonSprite::create(
|
||||
top, geodeTheme ? (altColor ? CircleBaseColor::DarkAqua : CircleBaseColor::DarkPurple) : CircleBaseColor::Green, size
|
||||
);
|
||||
|
@ -184,21 +189,16 @@ CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale, CircleBa
|
|||
return ret;
|
||||
}
|
||||
|
||||
ButtonSprite* createGeodeTagLabel(std::string const& text, std::optional<std::pair<ccColor3B, ccColor3B>> const& color) {
|
||||
ButtonSprite* createTagLabel(std::string const& text, std::pair<ccColor3B, ccColor3B> const& color) {
|
||||
auto label = ButtonSprite::create(text.c_str(), "bigFont.fnt", "white-square.png"_spr, .8f);
|
||||
if (color) {
|
||||
label->m_label->setColor(color->first);
|
||||
label->m_BGSprite->setColor(color->second);
|
||||
}
|
||||
else {
|
||||
auto def = geodeTagColor(text);
|
||||
label->m_label->setColor(def.first);
|
||||
label->m_BGSprite->setColor(def.second);
|
||||
}
|
||||
label->m_label->setColor(color.first);
|
||||
label->m_BGSprite->setColor(color.second);
|
||||
return label;
|
||||
}
|
||||
|
||||
std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text) {
|
||||
ButtonSprite* createGeodeTagLabel(std::string_view tag) {
|
||||
return createTagLabel(geodeTagName(tag), geodeTagColors(tag));
|
||||
}
|
||||
std::pair<ccColor3B, ccColor3B> geodeTagColors(std::string_view tag) {
|
||||
static std::array TAG_COLORS {
|
||||
std::make_pair(ccc3(240, 233, 255), ccc3(130, 123, 163)),
|
||||
std::make_pair(ccc3(234, 255, 245), ccc3(123, 163, 136)),
|
||||
|
@ -206,20 +206,32 @@ std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text) {
|
|||
std::make_pair(ccc3(255, 253, 240), ccc3(163, 157, 123)),
|
||||
std::make_pair(ccc3(255, 242, 240), ccc3(163, 128, 123)),
|
||||
};
|
||||
return TAG_COLORS[hash(text) % 5932 % TAG_COLORS.size()];
|
||||
}
|
||||
|
||||
ListBorders* createGeodeListBorders(CCSize const& size) {
|
||||
auto ret = ListBorders::create();
|
||||
if (isGeodeTheme()) {
|
||||
ret->setSpriteFrames("geode-list-top.png"_spr, "geode-list-side.png"_spr, 2);
|
||||
if (tag == "modtober24") {
|
||||
return std::make_pair(ccc3(225, 236, 245), ccc3(82, 139, 201));
|
||||
}
|
||||
ret->setContentSize(size);
|
||||
return ret;
|
||||
return TAG_COLORS[hash(tag) % 5932 % TAG_COLORS.size()];
|
||||
}
|
||||
std::string geodeTagName(std::string_view tag) {
|
||||
// todo in v4: rework tags to use a server-provided display name instead
|
||||
if (tag == "modtober24") {
|
||||
return "Modtober 2024";
|
||||
}
|
||||
// Everything else just capitalize and that's it
|
||||
auto readable = std::string(tag);
|
||||
readable[0] = std::toupper(readable[0]);
|
||||
return readable;
|
||||
}
|
||||
|
||||
bool isGeodeTheme() {
|
||||
return Mod::get()->template getSettingValue<bool>("enable-geode-theme");
|
||||
ListBorders* createGeodeListBorders(CCSize const& size, bool forceDisableTheme) {
|
||||
auto ret = ListBorders::create();
|
||||
const bool geodeTheme = isGeodeTheme(forceDisableTheme);
|
||||
if (geodeTheme) {
|
||||
ret->setSpriteFrames("geode-list-top.png"_spr, "geode-list-side.png"_spr, 2);
|
||||
ret->setContentSize(size);
|
||||
} else {
|
||||
ret->setContentSize(size + ccp(5, 5));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GeodeTabSprite::init(const char* iconFrame, const char* text, float width, bool altColor) {
|
||||
|
|
|
@ -16,11 +16,16 @@ enum class GeodePopupStyle {
|
|||
Alt2,
|
||||
};
|
||||
|
||||
bool isGeodeTheme(bool forceDisableTheme = false);
|
||||
|
||||
template <class... Args>
|
||||
class GeodePopup : public Popup<Args...> {
|
||||
protected:
|
||||
bool m_forceDisableTheme = false;
|
||||
|
||||
bool init(float width, float height, Args... args, GeodePopupStyle style = GeodePopupStyle::Default, bool forceDisableTheme = false) {
|
||||
const bool geodeTheme = !forceDisableTheme && Mod::get()->template getSettingValue<bool>("enable-geode-theme");
|
||||
m_forceDisableTheme = forceDisableTheme;
|
||||
const bool geodeTheme = isGeodeTheme(forceDisableTheme);
|
||||
const char* bg;
|
||||
switch (style) {
|
||||
default:
|
||||
|
@ -49,16 +54,17 @@ class GeodeSquareSprite : public CCSprite {
|
|||
protected:
|
||||
bool* m_stateSrc = nullptr;
|
||||
bool m_state = false;
|
||||
bool m_forceDisableTheme = false;
|
||||
CCSprite* m_topSprite;
|
||||
|
||||
bool init(CCSprite* top, bool* state);
|
||||
bool init(CCSprite* top, bool* state, bool forceDisableTheme = false);
|
||||
|
||||
void update(float dt) override;
|
||||
void updateImage();
|
||||
|
||||
public:
|
||||
static GeodeSquareSprite* create(const char* top, bool* state = nullptr);
|
||||
static GeodeSquareSprite* createWithSpriteFrameName(const char* top, bool* state = nullptr);
|
||||
static GeodeSquareSprite* create(const char* top, bool* state = nullptr, bool forceDisableTheme = false);
|
||||
static GeodeSquareSprite* createWithSpriteFrameName(const char* top, bool* state = nullptr, bool forceDisableTheme = false);
|
||||
|
||||
CCSprite* getTopSprite() const;
|
||||
void setState(bool state);
|
||||
|
@ -73,19 +79,19 @@ enum class GeodeButtonSprite {
|
|||
Enable,
|
||||
Gray,
|
||||
};
|
||||
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr);
|
||||
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg = GeodeButtonSprite::Default);
|
||||
ButtonSprite* createGeodeButton(std::string const& text, int width, bool absolute = false, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default);
|
||||
ButtonSprite* createGeodeButton(std::string const& text, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default);
|
||||
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr, bool forceDisableTheme = false);
|
||||
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg = GeodeButtonSprite::Default, bool forceDisableTheme = false);
|
||||
ButtonSprite* createGeodeButton(std::string const& text, int width, bool absolute = false, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default, bool forceDisableTheme = false);
|
||||
ButtonSprite* createGeodeButton(std::string const& text, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default, bool forceDisableTheme = false);
|
||||
|
||||
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale = 1.f, CircleBaseSize size = CircleBaseSize::Medium, bool altColor = false);
|
||||
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale = 1.f, CircleBaseSize size = CircleBaseSize::Medium, bool altColor = false, bool forceDisableTheme = false);
|
||||
|
||||
ButtonSprite* createGeodeTagLabel(std::string const& text, std::optional<std::pair<ccColor3B, ccColor3B>> const& color = std::nullopt);
|
||||
std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text);
|
||||
ButtonSprite* createTagLabel(std::string const& text, std::pair<ccColor3B, ccColor3B> const& color);
|
||||
ButtonSprite* createGeodeTagLabel(std::string_view tag);
|
||||
std::pair<ccColor3B, ccColor3B> geodeTagColors(std::string_view tag);
|
||||
std::string geodeTagName(std::string_view tag);
|
||||
|
||||
ListBorders* createGeodeListBorders(CCSize const& size);
|
||||
|
||||
bool isGeodeTheme();
|
||||
ListBorders* createGeodeListBorders(CCSize const& size, bool forceDisableTheme = false);
|
||||
|
||||
class GeodeTabSprite : public CCNode {
|
||||
protected:
|
||||
|
|
|
@ -430,14 +430,15 @@ bool ModsLayer::init() {
|
|||
// Increment touch priority so the mods in the list don't override
|
||||
mainTabs->setTouchPriority(-150);
|
||||
|
||||
for (auto item : std::initializer_list<std::tuple<const char*, const char*, ModListSource*, const char*>> {
|
||||
{ "download.png"_spr, "Installed", InstalledModListSource::get(InstalledModListType::All), "installed-button" },
|
||||
{ "GJ_starsIcon_001.png", "Featured", ServerModListSource::get(ServerModListType::Featured), "featured-button" },
|
||||
{ "globe.png"_spr, "Download", ServerModListSource::get(ServerModListType::Download), "download-button" },
|
||||
{ "GJ_timeIcon_001.png", "Recent", ServerModListSource::get(ServerModListType::Recent), "recent-button" },
|
||||
for (auto item : std::initializer_list<std::tuple<const char*, const char*, ModListSource*, const char*, bool>> {
|
||||
{ "download.png"_spr, "Installed", InstalledModListSource::get(InstalledModListType::All), "installed-button", false },
|
||||
{ "GJ_starsIcon_001.png", "Featured", ServerModListSource::get(ServerModListType::Featured), "featured-button", false },
|
||||
{ "globe.png"_spr, "Download", ServerModListSource::get(ServerModListType::Download), "download-button", false },
|
||||
{ "GJ_timeIcon_001.png", "Recent", ServerModListSource::get(ServerModListType::Recent), "recent-button", false },
|
||||
{ "d_artCloud_03_001.png", "Modtober", ServerModListSource::get(ServerModListType::Modtober24), "modtober-button", true },
|
||||
}) {
|
||||
auto btn = CCMenuItemSpriteExtra::create(
|
||||
GeodeTabSprite::create(std::get<0>(item), std::get<1>(item), 120),
|
||||
GeodeTabSprite::create(std::get<0>(item), std::get<1>(item), 100, std::get<4>(item)),
|
||||
this, menu_selector(ModsLayer::onTab)
|
||||
);
|
||||
btn->setUserData(std::get<2>(item));
|
||||
|
@ -683,7 +684,7 @@ void ModsLayer::onSearch(CCObject*) {
|
|||
}
|
||||
}
|
||||
void ModsLayer::onTheme(CCObject*) {
|
||||
auto old = Mod::get()->template getSettingValue<bool>("enable-geode-theme");
|
||||
auto old = Mod::get()->getSettingValue<bool>("enable-geode-theme");
|
||||
createQuickPopup(
|
||||
"Switch Theme",
|
||||
fmt::format(
|
||||
|
@ -704,7 +705,7 @@ void ModsLayer::onTheme(CCObject*) {
|
|||
);
|
||||
}
|
||||
void ModsLayer::onSettings(CCObject*) {
|
||||
openSettingsPopup(Mod::get());
|
||||
openSettingsPopup(Mod::get(), false);
|
||||
}
|
||||
|
||||
ModsLayer* ModsLayer::create() {
|
||||
|
|
|
@ -84,12 +84,12 @@ bool ModItem::init(ModSource&& source) {
|
|||
);
|
||||
m_infoContainer->addChild(m_developers);
|
||||
|
||||
m_restartRequiredLabel = createGeodeTagLabel(
|
||||
m_restartRequiredLabel = createTagLabel(
|
||||
"Restart Required",
|
||||
{{
|
||||
{
|
||||
to3B(ColorProvider::get()->color("mod-list-restart-required-label"_spr)),
|
||||
to3B(ColorProvider::get()->color("mod-list-restart-required-label-bg"_spr))
|
||||
}}
|
||||
}
|
||||
);
|
||||
m_restartRequiredLabel->setID("restart-required-label");
|
||||
m_restartRequiredLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
|
||||
|
@ -208,6 +208,11 @@ bool ModItem::init(ModSource&& source) {
|
|||
paidModLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
||||
m_titleContainer->addChild(paidModLabel);
|
||||
}
|
||||
if (metadata.tags.contains("modtober24")) {
|
||||
auto modtoberLabel = CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr);
|
||||
modtoberLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
|
||||
m_titleContainer->addChild(modtoberLabel);
|
||||
}
|
||||
|
||||
// Show mod download count here already so people can make informed decisions
|
||||
// on which mods to install
|
||||
|
@ -363,7 +368,10 @@ void ModItem::updateState() {
|
|||
m_bg->setColor("mod-list-paid-color"_cc3b);
|
||||
m_bg->setOpacity(55);
|
||||
}
|
||||
|
||||
if (metadata.tags.contains("modtober24")) {
|
||||
m_bg->setColor(ccc3(63, 91, 138));
|
||||
m_bg->setOpacity(85);
|
||||
}
|
||||
if (isGeodeTheme() && metadata.featured) {
|
||||
m_bg->setColor("mod-list-featured-color"_cc3b);
|
||||
m_bg->setOpacity(65);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../popups/SortPopup.hpp"
|
||||
#include "../GeodeStyle.hpp"
|
||||
#include "../ModsLayer.hpp"
|
||||
#include "../popups/ModtoberPopup.hpp"
|
||||
|
||||
bool ModList::init(ModListSource* src, CCSize const& size) {
|
||||
if (!CCNode::init())
|
||||
|
@ -249,6 +250,34 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
|
|||
|
||||
m_topContainer->addChild(m_searchMenu);
|
||||
|
||||
// Modtober banner; this can be removed after Modtober 2024 is over!
|
||||
if (
|
||||
auto src = typeinfo_cast<ServerModListSource*>(m_source);
|
||||
src && src->getType() == ServerModListType::Modtober24
|
||||
) {
|
||||
auto menu = CCMenu::create();
|
||||
menu->setID("modtober-banner");
|
||||
menu->ignoreAnchorPointForPosition(false);
|
||||
menu->setContentSize({ size.width, 30 });
|
||||
|
||||
auto banner = CCSprite::createWithSpriteFrameName("modtober24-banner.png"_spr);
|
||||
limitNodeWidth(banner, size.width, 1.f, .1f);
|
||||
menu->addChildAtPosition(banner, Anchor::Center);
|
||||
|
||||
auto label = CCLabelBMFont::create("Modtober 2024 is Here!", "bigFont.fnt");
|
||||
label->setScale(.5f);
|
||||
menu->addChildAtPosition(label, Anchor::Left, ccp(10, 0), ccp(0, .5f));
|
||||
|
||||
auto aboutSpr = createGeodeButton("About");
|
||||
aboutSpr->setScale(.5f);
|
||||
auto aboutBtn = CCMenuItemSpriteExtra::create(
|
||||
aboutSpr, this, menu_selector(ModList::onModtoberInfo)
|
||||
);
|
||||
menu->addChildAtPosition(aboutBtn, Anchor::Right, ccp(-35, 0));
|
||||
|
||||
m_topContainer->addChild(menu);
|
||||
}
|
||||
|
||||
m_topContainer->setLayout(
|
||||
ColumnLayout::create()
|
||||
->setGap(0)
|
||||
|
@ -501,6 +530,8 @@ void ModList::updateTopContainer() {
|
|||
static_cast<AxisLayout*>(m_list->m_contentLayer->getLayout())->getGap() :
|
||||
this->getContentHeight()
|
||||
);
|
||||
static_cast<ColumnLayout*>(m_list->m_contentLayer->getLayout())->setAutoGrowAxis(m_list->getContentHeight());
|
||||
m_list->m_contentLayer->updateLayout();
|
||||
|
||||
// Preserve relative scroll position
|
||||
m_list->m_contentLayer->setPositionY((
|
||||
|
@ -659,6 +690,9 @@ void ModList::onToggleErrors(CCObject*) {
|
|||
void ModList::onUpdateAll(CCObject*) {
|
||||
server::ModDownloadManager::get()->startUpdateAll();
|
||||
}
|
||||
void ModList::onModtoberInfo(CCObject*) {
|
||||
ModtoberPopup::create()->show();
|
||||
}
|
||||
|
||||
size_t ModList::getPage() const {
|
||||
return m_page;
|
||||
|
|
|
@ -71,6 +71,7 @@ protected:
|
|||
void onToggleUpdates(CCObject*);
|
||||
void onToggleErrors(CCObject*);
|
||||
void onUpdateAll(CCObject*);
|
||||
void onModtoberInfo(CCObject*);
|
||||
|
||||
public:
|
||||
static ModList* create(ModListSource* src, CCSize const& size);
|
||||
|
|
|
@ -131,7 +131,7 @@ bool FiltersPopup::setup(ModListSource* src) {
|
|||
m_mainLayer->addChildAtPosition(inputContainer, Anchor::Bottom, ccp(0, 60), ccp(.5f, .5f));
|
||||
}
|
||||
|
||||
auto okSpr = createGeodeButton("OK");
|
||||
auto okSpr = createGeodeButton("OK", false, GeodeButtonSprite::Default, m_forceDisableTheme);
|
||||
okSpr->setScale(.7f);
|
||||
auto okBtn = CCMenuItemSpriteExtra::create(
|
||||
okSpr, this, menu_selector(FiltersPopup::onClose)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "ModPopup.hpp"
|
||||
#include <Geode/binding/ButtonSprite.hpp>
|
||||
#include <Geode/ui/MDTextArea.hpp>
|
||||
#include <Geode/ui/TextInput.hpp>
|
||||
#include <Geode/utils/web.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/ModSettingsManager.hpp>
|
||||
|
@ -10,6 +11,7 @@
|
|||
#include "../settings/ModSettingsPopup.hpp"
|
||||
#include "../../../internal/about.hpp"
|
||||
#include "../../GeodeUIEvent.hpp"
|
||||
#include "../popups/ModtoberPopup.hpp"
|
||||
|
||||
class FetchTextArea : public CCNode {
|
||||
public:
|
||||
|
@ -300,12 +302,12 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
manageTitle->setOpacity(195);
|
||||
manageContainer->addChildAtPosition(manageTitle, Anchor::Left, ccp(0, 0), ccp(0, .5f));
|
||||
|
||||
m_restartRequiredLabel = createGeodeTagLabel(
|
||||
m_restartRequiredLabel = createTagLabel(
|
||||
"Restart Required",
|
||||
{{
|
||||
{
|
||||
to3B(ColorProvider::get()->color("mod-list-restart-required-label"_spr)),
|
||||
to3B(ColorProvider::get()->color("mod-list-restart-required-label-bg"_spr))
|
||||
}}
|
||||
}
|
||||
);
|
||||
m_restartRequiredLabel->setScale(.3f);
|
||||
manageContainer->addChildAtPosition(m_restartRequiredLabel, Anchor::Right, ccp(0, 0), ccp(1, .5f));
|
||||
|
@ -334,7 +336,8 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto updateModSpr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName("update.png"_spr),
|
||||
"Update",
|
||||
GeodeButtonSprite::Install
|
||||
GeodeButtonSprite::Install,
|
||||
m_forceDisableTheme
|
||||
);
|
||||
updateModSpr->setScale(.5f);
|
||||
m_updateBtn = CCMenuItemSpriteExtra::create(
|
||||
|
@ -345,13 +348,15 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto enableModOffSpr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName("GJ_completesIcon_001.png"),
|
||||
"Enable",
|
||||
GeodeButtonSprite::Enable
|
||||
GeodeButtonSprite::Enable,
|
||||
m_forceDisableTheme
|
||||
);
|
||||
enableModOffSpr->setScale(.5f);
|
||||
auto enableModOnSpr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png"),
|
||||
"Disable",
|
||||
GeodeButtonSprite::Delete
|
||||
GeodeButtonSprite::Delete,
|
||||
m_forceDisableTheme
|
||||
);
|
||||
enableModOnSpr->setScale(.5f);
|
||||
m_enableBtn = CCMenuItemToggler::create(
|
||||
|
@ -364,7 +369,8 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto reenableModOffSpr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName("reset.png"_spr),
|
||||
"Re-Enable",
|
||||
GeodeButtonSprite::Default
|
||||
GeodeButtonSprite::Default,
|
||||
m_forceDisableTheme
|
||||
);
|
||||
reenableModOffSpr->setScale(.5f);
|
||||
auto reenableModOnSpr = createGeodeButton(
|
||||
|
@ -383,7 +389,8 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto installModSpr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName("GJ_downloadsIcon_001.png"),
|
||||
"Install",
|
||||
GeodeButtonSprite::Install
|
||||
GeodeButtonSprite::Install,
|
||||
m_forceDisableTheme
|
||||
);
|
||||
installModSpr->setScale(.5f);
|
||||
m_installBtn = CCMenuItemSpriteExtra::create(
|
||||
|
@ -394,7 +401,8 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto uninstallModSpr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName("delete-white.png"_spr),
|
||||
"Uninstall",
|
||||
GeodeButtonSprite::Default
|
||||
GeodeButtonSprite::Default,
|
||||
m_forceDisableTheme
|
||||
);
|
||||
uninstallModSpr->setScale(.5f);
|
||||
m_uninstallBtn = CCMenuItemSpriteExtra::create(
|
||||
|
@ -405,7 +413,8 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto cancelDownloadSpr = createGeodeButton(
|
||||
CCSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png"),
|
||||
"Cancel",
|
||||
GeodeButtonSprite::Default
|
||||
GeodeButtonSprite::Default,
|
||||
m_forceDisableTheme
|
||||
);
|
||||
cancelDownloadSpr->setScale(.5f);
|
||||
m_cancelBtn = CCMenuItemSpriteExtra::create(
|
||||
|
@ -565,7 +574,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
m_settingsBG->setContentSize(ccp(35, 30) / linksBG->getScale());
|
||||
m_buttonMenu->addChildAtPosition(m_settingsBG, Anchor::BottomLeft, ccp(28, 25));
|
||||
|
||||
auto settingsSpr = createGeodeCircleButton(CCSprite::createWithSpriteFrameName("settings.png"_spr));
|
||||
auto settingsSpr = createGeodeCircleButton(CCSprite::createWithSpriteFrameName("settings.png"_spr), 1.f, CircleBaseSize::Medium, false, m_forceDisableTheme);
|
||||
settingsSpr->setScale(.6f);
|
||||
auto settingsBtn = CCMenuItemSpriteExtra::create(
|
||||
settingsSpr, this, menu_selector(ModPopup::onSettings)
|
||||
|
@ -880,10 +889,7 @@ void ModPopup::onLoadTags(typename server::ServerRequest<std::unordered_set<std:
|
|||
m_tags->removeAllChildren();
|
||||
|
||||
for (auto& tag : data) {
|
||||
auto readable = tag;
|
||||
readable[0] = std::toupper(readable[0]);
|
||||
auto colors = geodeTagColor(tag);
|
||||
m_tags->addChild(createGeodeTagLabel(readable));
|
||||
m_tags->addChild(createGeodeTagLabel(tag));
|
||||
}
|
||||
|
||||
if (data.empty()) {
|
||||
|
@ -891,6 +897,47 @@ void ModPopup::onLoadTags(typename server::ServerRequest<std::unordered_set<std:
|
|||
label->setOpacity(120);
|
||||
m_tags->addChild(label);
|
||||
}
|
||||
// This should probably be kept even after modtober ends,
|
||||
// so the banner sprite must be kept
|
||||
// If the build times from the cool popup become too long then we can
|
||||
// probably move that to a normal FLAlert that explains "Modtober was
|
||||
// this contest blah blah this mod was made for it"
|
||||
else if (data.contains("modtober24")) {
|
||||
auto menu = CCMenu::create();
|
||||
menu->setID("modtober-banner");
|
||||
menu->ignoreAnchorPointForPosition(false);
|
||||
menu->setContentSize({ m_rightColumn->getContentWidth(), 25 });
|
||||
|
||||
auto banner = CCSprite::createWithSpriteFrameName("modtober24-banner-2.png"_spr);
|
||||
limitNodeWidth(banner, m_rightColumn->getContentWidth(), 1.f, .1f);
|
||||
menu->addChildAtPosition(banner, Anchor::Center);
|
||||
|
||||
auto label = CCLabelBMFont::create("Entry for Modtober 2024", "bigFont.fnt");
|
||||
label->setScale(.35f);
|
||||
menu->addChildAtPosition(label, Anchor::Left, ccp(10, 0), ccp(0, .5f));
|
||||
|
||||
auto aboutSpr = createGeodeButton("About", false, GeodeButtonSprite::Default, m_forceDisableTheme);
|
||||
aboutSpr->setScale(.35f);
|
||||
auto aboutBtn = CCMenuItemSpriteExtra::create(
|
||||
aboutSpr, this, menu_selector(ModPopup::onModtoberInfo)
|
||||
);
|
||||
menu->addChildAtPosition(aboutBtn, Anchor::Right, ccp(-25, 0));
|
||||
|
||||
m_rightColumn->addChildAtPosition(menu, Anchor::Bottom, ccp(0, 0), ccp(.5f, 0));
|
||||
|
||||
m_modtoberBanner = menu;
|
||||
|
||||
// Force reload of all the tabs since otherwise their contents will overflow
|
||||
for (auto& [_, tab] : m_tabs) {
|
||||
if (tab.second && tab.second->getParent()) {
|
||||
tab.second->removeFromParent();
|
||||
}
|
||||
tab.second = nullptr;
|
||||
}
|
||||
// This might cause a minor inconvenience to someone who opens the popup and
|
||||
// immediately switches to changelog but is then forced back into details
|
||||
this->loadTab(Tab::Details);
|
||||
}
|
||||
|
||||
m_tags->updateLayout();
|
||||
|
||||
|
@ -920,12 +967,17 @@ void ModPopup::loadTab(ModPopup::Tab tab) {
|
|||
btn.first->select(value == tab);
|
||||
}
|
||||
|
||||
float modtoberBannerHeight = 0;
|
||||
if (m_modtoberBanner) {
|
||||
modtoberBannerHeight = 30;
|
||||
}
|
||||
|
||||
if (auto existing = m_tabs.at(tab).second) {
|
||||
m_currentTabPage = existing;
|
||||
m_rightColumn->addChildAtPosition(existing, Anchor::Bottom);
|
||||
m_rightColumn->addChildAtPosition(existing, Anchor::Bottom, ccp(0, modtoberBannerHeight));
|
||||
}
|
||||
else {
|
||||
const auto size = (m_rightColumn->getContentSize() - ccp(0, 30));
|
||||
const auto size = (m_rightColumn->getContentSize() - ccp(0, 30 + modtoberBannerHeight));
|
||||
const float mdScale = .85f;
|
||||
switch (tab) {
|
||||
case Tab::Details: {
|
||||
|
@ -955,7 +1007,7 @@ void ModPopup::loadTab(ModPopup::Tab tab) {
|
|||
} break;
|
||||
}
|
||||
m_currentTabPage->setAnchorPoint({ .5f, .0f });
|
||||
m_rightColumn->addChildAtPosition(m_currentTabPage, Anchor::Bottom);
|
||||
m_rightColumn->addChildAtPosition(m_currentTabPage, Anchor::Bottom, ccp(0, modtoberBannerHeight));
|
||||
m_tabs.at(tab).second = m_currentTabPage;
|
||||
}
|
||||
}
|
||||
|
@ -1030,6 +1082,12 @@ void ModPopup::onLink(CCObject* sender) {
|
|||
void ModPopup::onSupport(CCObject*) {
|
||||
openSupportPopup(m_source.getMetadata());
|
||||
}
|
||||
void ModPopup::onModtoberInfo(CCObject*) {
|
||||
// todo: if we want to get rid of the modtober popup sprite (because it's fucking massive)
|
||||
// then we can just replace this with a normal FLAlert explaining
|
||||
// "this mod was an entry for modtober 2024 blah blah blah"
|
||||
ModtoberPopup::create()->show();
|
||||
}
|
||||
|
||||
ModPopup* ModPopup::create(ModSource&& src) {
|
||||
auto ret = new ModPopup();
|
||||
|
|
|
@ -35,6 +35,7 @@ protected:
|
|||
ButtonSprite* m_restartRequiredLabel;
|
||||
CCNode* m_rightColumn;
|
||||
CCNode* m_currentTabPage = nullptr;
|
||||
CCNode* m_modtoberBanner = nullptr;
|
||||
std::unordered_map<Tab, std::pair<GeodeTabSprite*, Ref<CCNode>>> m_tabs;
|
||||
EventListener<server::ServerRequest<server::ServerModMetadata>> m_statsListener;
|
||||
EventListener<server::ServerRequest<std::unordered_set<std::string>>> m_tagsListener;
|
||||
|
@ -63,6 +64,7 @@ protected:
|
|||
void onSettings(CCObject*);
|
||||
void onLink(CCObject*);
|
||||
void onSupport(CCObject*);
|
||||
void onModtoberInfo(CCObject*);
|
||||
|
||||
public:
|
||||
void loadTab(Tab tab);
|
||||
|
|
44
loader/src/ui/mods/popups/ModtoberPopup.cpp
Normal file
44
loader/src/ui/mods/popups/ModtoberPopup.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "ModtoberPopup.hpp"
|
||||
#include <Geode/utils/web.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/binding/ButtonSprite.hpp>
|
||||
|
||||
bool ModtoberPopup::setup() {
|
||||
m_bgSprite->setVisible(false);
|
||||
|
||||
auto bg = CCSprite::createWithSpriteFrameName("modtober24-popup.png"_spr);
|
||||
m_mainLayer->addChildAtPosition(bg, Anchor::Center);
|
||||
|
||||
auto supportSpr = createGeodeButton("Join", false, GeodeButtonSprite::Default, m_forceDisableTheme);
|
||||
supportSpr->setScale(.8f);
|
||||
auto supportBtn = CCMenuItemSpriteExtra::create(
|
||||
supportSpr, this, menu_selector(ModtoberPopup::onDiscord)
|
||||
);
|
||||
m_buttonMenu->addChildAtPosition(supportBtn, Anchor::BottomRight, ccp(-65, 50));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModtoberPopup::onDiscord(CCObject*) {
|
||||
createQuickPopup(
|
||||
"Join Modtober",
|
||||
"<cf>Modtober</c> is being hosted on the <cg>GD Programming</c> <ca>Discord Server</c>.\n"
|
||||
"To participate, join GDP and read the rules for the contest in <co>#modtober-2024</c>",
|
||||
"Cancel", "Join Discord",
|
||||
[](auto, bool btn2) {
|
||||
if (btn2) {
|
||||
web::openLinkInBrowser("https://discord.gg/gd-programming-646101505417674758");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ModtoberPopup* ModtoberPopup::create() {
|
||||
auto ret = new ModtoberPopup();
|
||||
if (ret && ret->init(410, 270)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
CC_SAFE_DELETE(ret);
|
||||
return nullptr;
|
||||
}
|
16
loader/src/ui/mods/popups/ModtoberPopup.hpp
Normal file
16
loader/src/ui/mods/popups/ModtoberPopup.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/ui/Popup.hpp>
|
||||
#include "../GeodeStyle.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
class ModtoberPopup : public GeodePopup<> {
|
||||
protected:
|
||||
bool setup() override;
|
||||
|
||||
void onDiscord(CCObject*);
|
||||
|
||||
public:
|
||||
static ModtoberPopup* create();
|
||||
};
|
|
@ -6,6 +6,7 @@
|
|||
#include <Geode/ui/ScrollLayer.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/ui/General.hpp>
|
||||
#include <Geode/ui/Scrollbar.hpp>
|
||||
#include <loader/SettingNodeV3.hpp>
|
||||
// needed for weightedFuzzyMatch
|
||||
#include <ui/mods/sources/ModListSource.hpp>
|
||||
|
@ -25,10 +26,7 @@ static bool matchSearch(SettingNodeV3* node, std::string const& query) {
|
|||
else {
|
||||
addToList |= weightedFuzzyMatch(setting->getKey(), query, 1, weighted);
|
||||
}
|
||||
if (auto desc = setting->getDescription()) {
|
||||
addToList |= weightedFuzzyMatch(*desc, query, 0.02, weighted);
|
||||
}
|
||||
if (weighted < 2) {
|
||||
if (weighted < 60 + 10 * query.size()) {
|
||||
addToList = false;
|
||||
}
|
||||
return addToList;
|
||||
|
@ -62,7 +60,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
m_searchInput->setID("search-input");
|
||||
searchContainer->addChildAtPosition(m_searchInput, Anchor::Left, ccp(7.5f, 0), ccp(0, .5f));
|
||||
|
||||
auto searchClearSpr = GeodeSquareSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png");
|
||||
auto searchClearSpr = GeodeSquareSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png", nullptr, m_forceDisableTheme);
|
||||
searchClearSpr->setScale(.45f);
|
||||
m_searchClearBtn = CCMenuItemSpriteExtra::create(
|
||||
searchClearSpr, this, menu_selector(ModSettingsPopup::onClearSearch)
|
||||
|
@ -90,7 +88,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
m_list->m_contentLayer->setLayout(
|
||||
ColumnLayout::create()
|
||||
->setAxisReverse(true)
|
||||
->setAutoGrowAxis(layerSize.height)
|
||||
->setAutoGrowAxis(m_list->getContentHeight())
|
||||
->setCrossAxisOverflow(false)
|
||||
->setAxisAlignment(AxisAlignment::End)
|
||||
->setGap(0)
|
||||
|
@ -106,7 +104,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
|
||||
// layer borders
|
||||
|
||||
m_mainLayer->addChildAtPosition(createGeodeListBorders(layerSize), Anchor::Center);
|
||||
m_mainLayer->addChildAtPosition(createGeodeListBorders(layerSize, m_forceDisableTheme), Anchor::Center);
|
||||
|
||||
auto scrollBar = Scrollbar::create(m_list);
|
||||
m_mainLayer->addChildAtPosition(
|
||||
|
@ -121,14 +119,14 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
m_applyMenu->getLayout()->ignoreInvisibleChildren(true);
|
||||
m_applyMenu->setTouchPriority(buttonPriority);
|
||||
|
||||
auto restartBtnSpr = createGeodeButton("Restart Now", true);
|
||||
auto restartBtnSpr = createGeodeButton("Restart Now", true, GeodeButtonSprite::Default, m_forceDisableTheme);
|
||||
restartBtnSpr->setScale(.6f);
|
||||
m_restartBtn = CCMenuItemSpriteExtra::create(
|
||||
restartBtnSpr, this, menu_selector(ModSettingsPopup::onRestart)
|
||||
);
|
||||
m_applyMenu->addChildAtPosition(m_restartBtn, Anchor::Bottom, ccp(0, 20));
|
||||
|
||||
m_applyBtnSpr = createGeodeButton("Apply", true);
|
||||
m_applyBtnSpr = createGeodeButton("Apply", true, GeodeButtonSprite::Default, m_forceDisableTheme);
|
||||
m_applyBtnSpr->setScale(.6f);
|
||||
m_applyBtn = CCMenuItemSpriteExtra::create(
|
||||
m_applyBtnSpr, this, menu_selector(ModSettingsPopup::onApply)
|
||||
|
@ -137,7 +135,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
|
||||
m_mainLayer->addChildAtPosition(m_applyMenu, Anchor::Bottom, ccp(0, 20));
|
||||
|
||||
auto resetBtnSpr = createGeodeButton("Reset All", true);
|
||||
auto resetBtnSpr = createGeodeButton("Reset All", true, GeodeButtonSprite::Default, m_forceDisableTheme);
|
||||
resetBtnSpr->setScale(.6f);
|
||||
|
||||
auto resetBtn = CCMenuItemSpriteExtra::create(
|
||||
|
@ -162,7 +160,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
folderSprSub->setOpacity(155);
|
||||
folderSprSub->setScale(.55f);
|
||||
folderSpr->addChildAtPosition(folderSprSub, Anchor::Center, ccp(0, -3));
|
||||
auto buttonSpr = createGeodeButton(folderSpr, "");
|
||||
auto buttonSpr = createGeodeButton(folderSpr, "", GeodeButtonSprite::Default, m_forceDisableTheme);
|
||||
buttonSpr->setScale(.6f);
|
||||
buttonSpr->getIcon()->setScale(buttonSpr->getIcon()->getScale() * 1.4f);
|
||||
auto folderBtn = CCMenuItemSpriteExtra::create(
|
||||
|
@ -345,9 +343,9 @@ void ModSettingsPopup::onClose(CCObject* sender) {
|
|||
GeodePopup::onClose(sender);
|
||||
}
|
||||
|
||||
ModSettingsPopup* ModSettingsPopup::create(Mod* mod) {
|
||||
ModSettingsPopup* ModSettingsPopup::create(Mod* mod, bool forceDisableTheme) {
|
||||
auto ret = new ModSettingsPopup();
|
||||
if (ret->init(440, 280, mod)) {
|
||||
if (ret->init(440, 280, mod, GeodePopupStyle::Default, forceDisableTheme)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/ui/Popup.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/ui/ScrollLayer.hpp>
|
||||
#include <Geode/ui/TextInput.hpp>
|
||||
|
||||
#include "../GeodeStyle.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
@ -33,5 +36,5 @@ protected:
|
|||
void onClearSearch(CCObject*);
|
||||
|
||||
public:
|
||||
static ModSettingsPopup* create(Mod* mod);
|
||||
static ModSettingsPopup* create(Mod* mod, bool forceDisableTheme = false);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <server/Server.hpp>
|
||||
#include "../list/ModItem.hpp"
|
||||
|
||||
|
@ -143,6 +144,7 @@ enum class ServerModListType {
|
|||
Featured,
|
||||
Trending,
|
||||
Recent,
|
||||
Modtober24,
|
||||
};
|
||||
|
||||
class ServerModListSource : public ModListSource {
|
||||
|
@ -165,6 +167,8 @@ public:
|
|||
server::ModsQuery const& getQuery() const;
|
||||
InvalidateQueryAfter<server::ModsQuery> getQueryMut();
|
||||
bool isDefaultQuery() const override;
|
||||
server::ModsQuery createDefaultQuery() const;
|
||||
ServerModListType getType() const;
|
||||
};
|
||||
|
||||
class ModPackListSource : public ModListSource {
|
||||
|
@ -191,6 +195,7 @@ void filterModsWithLocalQuery(ModListSource::ProvidedMods& mods, Query const& qu
|
|||
std::vector<std::pair<ModSource, double>> filtered;
|
||||
|
||||
// Filter installed mods based on query
|
||||
// TODO: maybe skip fuzzy matching altogether if query is empty?
|
||||
for (auto& src : mods.mods) {
|
||||
double weighted = 0;
|
||||
bool addToList = true;
|
||||
|
@ -223,7 +228,10 @@ void filterModsWithLocalQuery(ModListSource::ProvidedMods& mods, Query const& qu
|
|||
return a.second > b.second;
|
||||
}
|
||||
// Sort secondarily alphabetically
|
||||
return a.first.getMetadata().getName() < b.first.getMetadata().getName();
|
||||
return utils::string::caseInsensitiveCompare(
|
||||
a.first.getMetadata().getName(),
|
||||
b.first.getMetadata().getName()
|
||||
) == std::strong_ordering::less;
|
||||
});
|
||||
|
||||
mods.mods.clear();
|
||||
|
|
|
@ -1,29 +1,7 @@
|
|||
#include "ModListSource.hpp"
|
||||
|
||||
void ServerModListSource::resetQuery() {
|
||||
switch (m_type) {
|
||||
case ServerModListType::Download: {
|
||||
m_query = server::ModsQuery {};
|
||||
} break;
|
||||
|
||||
case ServerModListType::Featured: {
|
||||
m_query = server::ModsQuery {
|
||||
.featured = true,
|
||||
};
|
||||
} break;
|
||||
|
||||
case ServerModListType::Trending: {
|
||||
m_query = server::ModsQuery {
|
||||
.sorting = server::ModsSort::RecentlyUpdated,
|
||||
};
|
||||
} break;
|
||||
|
||||
case ServerModListType::Recent: {
|
||||
m_query = server::ModsQuery {
|
||||
.sorting = server::ModsSort::RecentlyPublished,
|
||||
};
|
||||
} break;
|
||||
}
|
||||
m_query = this->createDefaultQuery();
|
||||
}
|
||||
|
||||
ServerModListSource::ProviderTask ServerModListSource::fetchPage(size_t page, size_t pageSize, bool forceUpdate) {
|
||||
|
@ -56,7 +34,7 @@ ServerModListSource::ServerModListSource(ServerModListType type)
|
|||
|
||||
ServerModListSource* ServerModListSource::get(ServerModListType type) {
|
||||
switch (type) {
|
||||
default:
|
||||
default: [[fallthrough]];
|
||||
case ServerModListType::Download: {
|
||||
static auto inst = new ServerModListSource(ServerModListType::Download);
|
||||
return inst;
|
||||
|
@ -76,6 +54,11 @@ ServerModListSource* ServerModListSource::get(ServerModListType type) {
|
|||
static auto inst = new ServerModListSource(ServerModListType::Recent);
|
||||
return inst;
|
||||
} break;
|
||||
|
||||
case ServerModListType::Modtober24: {
|
||||
static auto inst = new ServerModListSource(ServerModListType::Modtober24);
|
||||
return inst;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +87,31 @@ InvalidateQueryAfter<server::ModsQuery> ServerModListSource::getQueryMut() {
|
|||
return InvalidateQueryAfter(m_query, this);
|
||||
}
|
||||
bool ServerModListSource::isDefaultQuery() const {
|
||||
return !m_query.query.has_value() &&
|
||||
m_query.tags.empty() &&
|
||||
!m_query.developer.has_value();
|
||||
return m_query == this->createDefaultQuery();
|
||||
}
|
||||
|
||||
server::ModsQuery ServerModListSource::createDefaultQuery() const {
|
||||
switch (m_type) {
|
||||
case ServerModListType::Download: return server::ModsQuery {};
|
||||
|
||||
case ServerModListType::Featured: return server::ModsQuery {
|
||||
.featured = true,
|
||||
};
|
||||
|
||||
case ServerModListType::Trending: return server::ModsQuery {
|
||||
.sorting = server::ModsSort::RecentlyUpdated,
|
||||
};
|
||||
|
||||
case ServerModListType::Recent: return server::ModsQuery {
|
||||
.sorting = server::ModsSort::RecentlyPublished,
|
||||
};
|
||||
|
||||
case ServerModListType::Modtober24: return server::ModsQuery {
|
||||
.tags = { "modtober24" },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ServerModListType ServerModListSource::getType() const {
|
||||
return m_type;
|
||||
}
|
||||
|
|
|
@ -151,14 +151,14 @@ ListBorders* ListBorders::create() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void ListBorders::setSpriteFrames(const char* topAndBottom, const char* side, float topPadding) {
|
||||
void ListBorders::setSpriteFrames(const char* topAndBottom, const char* side, float horizontalPadding) {
|
||||
this->setSprites(
|
||||
CCScale9Sprite::createWithSpriteFrameName(topAndBottom),
|
||||
CCScale9Sprite::createWithSpriteFrameName(topAndBottom),
|
||||
CCSprite::createWithSpriteFrameName(side),
|
||||
CCSprite::createWithSpriteFrameName(side),
|
||||
topPadding,
|
||||
topPadding
|
||||
horizontalPadding,
|
||||
horizontalPadding
|
||||
);
|
||||
m_bottom->setScaleY(-1);
|
||||
m_right->setFlipX(true);
|
||||
|
|
|
@ -151,7 +151,6 @@ void TextInput::setCallback(std::function<void(std::string const&)> onInput) {
|
|||
m_onInput = onInput;
|
||||
}
|
||||
void TextInput::setEnabled(bool enabled) {
|
||||
m_input->setMouseEnabled(enabled);
|
||||
m_input->setTouchEnabled(enabled);
|
||||
m_input->m_placeholderLabel->setOpacity(enabled ? 255 : 150);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <Geode/utils/general.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
|
|
@ -192,3 +192,20 @@ std::string utils::string::normalize(std::string const& str) {
|
|||
auto ret = str;
|
||||
return utils::string::normalizeIP(ret);
|
||||
}
|
||||
|
||||
std::strong_ordering utils::string::caseInsensitiveCompare(std::string_view str1, std::string_view str2) {
|
||||
for (size_t i = 0; i < str1.size() && i < str2.size(); i++) {
|
||||
auto const a = std::tolower(str1[i]);
|
||||
auto const b = std::tolower(str2[i]);
|
||||
if (a < b) {
|
||||
return std::strong_ordering::less;
|
||||
} else if (a > b) {
|
||||
return std::strong_ordering::greater;
|
||||
}
|
||||
}
|
||||
if (str1.size() < str2.size())
|
||||
return std::strong_ordering::less;
|
||||
else if (str1.size() > str2.size())
|
||||
return std::strong_ordering::greater;
|
||||
return std::strong_ordering::equal;
|
||||
}
|
Loading…
Reference in a new issue