Compare commits

...

3 commits

Author SHA1 Message Date
HJfod
5263483b0a bool setting nodes are so back now 2024-08-21 23:35:31 +03:00
HJfod
40a28eec7b make custom settings be based on custom setting types 2024-08-21 21:41:44 +03:00
HJfod
a5f56cb7cb a bit of docs on modsettingsmanager 2024-08-20 13:18:59 +03:00
14 changed files with 596 additions and 400 deletions

View file

@ -191,11 +191,6 @@ namespace geode {
* @see addCustomSetting * @see addCustomSetting
*/ */
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value); void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
/**
* Register a custom setting
*/
Result<> registerCustomSettingV3(std::string_view const key, std::shared_ptr<SettingV3> value);
/** /**
* Register a custom setting's value class. The new SettingValue class * Register a custom setting's value class. The new SettingValue class
* will be created in-place using `std::make_unique`. See * will be created in-place using `std::make_unique`. See
@ -213,6 +208,11 @@ namespace geode {
this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value)); this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value));
} }
/**
* Register a custom setting type
*/
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
/** /**
* Returns a prefixed launch argument name. See `Mod::getLaunchArgument` * Returns a prefixed launch argument name. See `Mod::getLaunchArgument`
* for details about mod-specific launch arguments. * for details about mod-specific launch arguments.

View file

@ -0,0 +1,46 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include "SettingV3.hpp"
namespace geode {
class GEODE_DLL ModSettingsManager final {
private:
class Impl;
std::unique_ptr<Impl> m_impl;
public:
static ModSettingsManager* from(Mod* mod);
ModSettingsManager(ModMetadata const& metadata);
~ModSettingsManager();
ModSettingsManager(ModSettingsManager&&);
ModSettingsManager(ModSettingsManager const&) = delete;
/**
* Load setting values from savedata.
* The format of the savedata should be an object with the keys being
* setting IDs and then the values the values of the saved settings
* @returns Ok if no horrible errors happened. Note that a setting value
* missing is not considered a horrible error, but will instead just log a
* warning into the console!
*/
Result<> load(matjson::Value const& json);
/**
* Save setting values to savedata.
* The format of the savedata will be an object with the keys being
* setting IDs and then the values the values of the saved settings
* @note If saving a setting fails, it will log a warning to the console
*/
void save(matjson::Value& json);
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
// todo in v4: remove this
Result<> registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr);
std::shared_ptr<SettingV3> get(std::string_view key);
std::shared_ptr<SettingValue> getLegacy(std::string_view key);
std::optional<Setting> getLegacyDefinition(std::string_view key);
};
}

View file

@ -9,11 +9,11 @@
// this unfortunately has to be included because of C++ templates // this unfortunately has to be included because of C++ templates
#include "../utils/JsonValidation.hpp" #include "../utils/JsonValidation.hpp"
// todo in v4: these can be removed as well as the friend decl in UnresolvedCustomSettingV3 // todo in v4: these can be removed as well as the friend decl in LegacyCustomSettingV3
class ModSettingsManager;
class LegacyCustomSettingToV3Node; class LegacyCustomSettingToV3Node;
namespace geode { namespace geode {
class ModSettingsManager;
class SettingNodeV3; class SettingNodeV3;
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> { class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
@ -22,9 +22,9 @@ namespace geode {
std::shared_ptr<GeodeImpl> m_impl; std::shared_ptr<GeodeImpl> m_impl;
protected: protected:
virtual Result<> onParse( void init(std::string const& key, std::string const& modID);
std::string const& key, std::string const& modID, matjson::Value const& json Result<> parseSharedProperties(std::string const& key, std::string const& modID, matjson::Value const& value, bool onlyNameAndDesc = false);
) = 0; void parseSharedProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc = false);
public: public:
SettingV3(); SettingV3();
@ -43,8 +43,23 @@ namespace geode {
* while the mod is still being initialized * while the mod is still being initialized
*/ */
Mod* getMod() const; Mod* getMod() const;
/**
* Get the name of this setting
*/
std::optional<std::string> getName() const;
/**
* Get the description of this setting
*/
std::optional<std::string> getDescription() const;
/**
* Get the "enable-if" scheme for this setting
*/
std::optional<std::string> getEnableIf() const;
/**
* Whether this setting requires a restart on change
*/
bool requiresRestart() const;
Result<> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
virtual bool load(matjson::Value const& json) = 0; virtual bool load(matjson::Value const& json) = 0;
virtual bool save(matjson::Value& json) const = 0; virtual bool save(matjson::Value& json) const = 0;
virtual SettingNodeV3* createNode(float width) = 0; virtual SettingNodeV3* createNode(float width) = 0;
@ -59,26 +74,22 @@ namespace geode {
virtual std::optional<Setting> convertToLegacy() const; virtual std::optional<Setting> convertToLegacy() const;
[[deprecated("This function will be removed alongside legacy settings in 4.0.0!")]] [[deprecated("This function will be removed alongside legacy settings in 4.0.0!")]]
virtual std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const; virtual std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const;
static Result<std::shared_ptr<SettingV3>> parseBuiltin(
std::string const& key, std::string const& modID, matjson::Value const& json
);
}; };
using SettingGenerator = std::function<Result<std::shared_ptr<SettingV3>>(
std::string const& key,
std::string const& modID,
matjson::Value const& json
)>;
namespace detail { namespace detail {
class GEODE_DLL GeodeSettingBaseV3 : public SettingV3 { template <class T, class V = T>
private: class GeodeSettingBaseValueV3 : public SettingV3 {
class Impl;
std::shared_ptr<Impl> m_impl;
Result<> parseSharedBase(JsonExpectedValue& json);
protected: protected:
Result<> isValidShared() const; virtual T& getValueMut() const = 0;
template <class T> template <class T>
Result<> parseShared(JsonExpectedValue& json, T& defaultValue) { void parseDefaultValue(JsonExpectedValue& json, T& defaultValue) {
GEODE_UNWRAP(this->parseSharedBase(json));
auto value = json.needs("default"); auto value = json.needs("default");
// Check if this is a platform-specific default value // Check if this is a platform-specific default value
if (value.isObject() && value.has(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)) { if (value.isObject() && value.has(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)) {
@ -87,22 +98,8 @@ namespace geode {
else { else {
value.into(defaultValue); value.into(defaultValue);
} }
return Ok();
} }
public:
GeodeSettingBaseV3();
std::string getName() const;
std::optional<std::string> getDescription() const;
std::optional<std::string> getEnableIf() const;
};
template <class T, class V = T>
class GeodeSettingBaseValueV3 : public GeodeSettingBaseV3 {
protected:
virtual T& getValueMut() const = 0;
public: public:
using ValueType = T; using ValueType = T;
@ -130,17 +127,13 @@ namespace geode {
class Impl; class Impl;
std::shared_ptr<Impl> m_impl; std::shared_ptr<Impl> m_impl;
protected:
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
friend class SettingV3; friend class SettingV3;
public: public:
TitleSettingV3(PrivateMarker); TitleSettingV3(PrivateMarker);
static Result<std::shared_ptr<TitleSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
std::string getTitle() const;
bool load(matjson::Value const& json) override; bool load(matjson::Value const& json) override;
bool save(matjson::Value& json) const override; bool save(matjson::Value& json) const override;
@ -150,23 +143,25 @@ namespace geode {
void reset() override; void reset() override;
}; };
class GEODE_DLL UnresolvedCustomSettingV3 final : public SettingV3 { // todo in v4: remove this class completely
class GEODE_DLL LegacyCustomSettingV3 final : public SettingV3 {
private: private:
class Impl; class Impl;
std::shared_ptr<Impl> m_impl; std::shared_ptr<Impl> m_impl;
friend class ::ModSettingsManager; friend class ::geode::ModSettingsManager;
friend class ::LegacyCustomSettingToV3Node; friend class ::LegacyCustomSettingToV3Node;
protected:
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
friend class SettingV3; friend class SettingV3;
public: public:
UnresolvedCustomSettingV3(PrivateMarker); LegacyCustomSettingV3(PrivateMarker);
static Result<std::shared_ptr<LegacyCustomSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
std::shared_ptr<SettingValue> getValue() const;
void setValue(std::shared_ptr<SettingValue> value);
bool load(matjson::Value const& json) override; bool load(matjson::Value const& json) override;
bool save(matjson::Value& json) const override; bool save(matjson::Value& json) const override;
@ -186,7 +181,6 @@ namespace geode {
protected: protected:
bool& getValueMut() const override; bool& getValueMut() const override;
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
@ -194,6 +188,7 @@ namespace geode {
public: public:
BoolSettingV3(PrivateMarker); BoolSettingV3(PrivateMarker);
static Result<std::shared_ptr<BoolSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
bool getDefaultValue() const override; bool getDefaultValue() const override;
Result<> isValid(bool value) const override; Result<> isValid(bool value) const override;
@ -213,7 +208,6 @@ namespace geode {
protected: protected:
int64_t& getValueMut() const override; int64_t& getValueMut() const override;
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
@ -221,6 +215,7 @@ namespace geode {
public: public:
IntSettingV3(PrivateMarker); IntSettingV3(PrivateMarker);
static Result<std::shared_ptr<IntSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
int64_t getDefaultValue() const override; int64_t getDefaultValue() const override;
Result<> isValid(int64_t value) const override; Result<> isValid(int64_t value) const override;
@ -251,7 +246,6 @@ namespace geode {
protected: protected:
double& getValueMut() const override; double& getValueMut() const override;
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
@ -259,6 +253,7 @@ namespace geode {
public: public:
FloatSettingV3(PrivateMarker); FloatSettingV3(PrivateMarker);
static Result<std::shared_ptr<FloatSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
double getDefaultValue() const override; double getDefaultValue() const override;
Result<> isValid(double value) const override; Result<> isValid(double value) const override;
@ -289,7 +284,6 @@ namespace geode {
protected: protected:
std::string& getValueMut() const override; std::string& getValueMut() const override;
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
@ -297,6 +291,7 @@ namespace geode {
public: public:
StringSettingV3(PrivateMarker); StringSettingV3(PrivateMarker);
static Result<std::shared_ptr<StringSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
std::string getDefaultValue() const override; std::string getDefaultValue() const override;
Result<> isValid(std::string_view value) const override; Result<> isValid(std::string_view value) const override;
@ -320,7 +315,6 @@ namespace geode {
protected: protected:
std::filesystem::path& getValueMut() const override; std::filesystem::path& getValueMut() const override;
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
@ -328,6 +322,7 @@ namespace geode {
public: public:
FileSettingV3(PrivateMarker); FileSettingV3(PrivateMarker);
static Result<std::shared_ptr<FileSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
std::filesystem::path getDefaultValue() const override; std::filesystem::path getDefaultValue() const override;
Result<> isValid(std::filesystem::path const& value) const override; Result<> isValid(std::filesystem::path const& value) const override;
@ -349,7 +344,6 @@ namespace geode {
protected: protected:
cocos2d::ccColor3B& getValueMut() const override; cocos2d::ccColor3B& getValueMut() const override;
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
@ -357,6 +351,7 @@ namespace geode {
public: public:
Color3BSettingV3(PrivateMarker); Color3BSettingV3(PrivateMarker);
static Result<std::shared_ptr<Color3BSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
cocos2d::ccColor3B getDefaultValue() const override; cocos2d::ccColor3B getDefaultValue() const override;
Result<> isValid(cocos2d::ccColor3B value) const override; Result<> isValid(cocos2d::ccColor3B value) const override;
@ -376,7 +371,6 @@ namespace geode {
protected: protected:
cocos2d::ccColor4B& getValueMut() const override; cocos2d::ccColor4B& getValueMut() const override;
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
private: private:
class PrivateMarker {}; class PrivateMarker {};
@ -384,6 +378,7 @@ namespace geode {
public: public:
Color4BSettingV3(PrivateMarker); Color4BSettingV3(PrivateMarker);
static Result<std::shared_ptr<Color4BSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
cocos2d::ccColor4B getDefaultValue() const override; cocos2d::ccColor4B getDefaultValue() const override;
Result<> isValid(cocos2d::ccColor4B value) const override; Result<> isValid(cocos2d::ccColor4B value) const override;
@ -404,6 +399,8 @@ namespace geode {
protected: protected:
bool init(std::shared_ptr<SettingV3> setting, float width); bool init(std::shared_ptr<SettingV3> setting, float width);
virtual void updateState();
/** /**
* Mark this setting as changed. This updates the UI for committing * Mark this setting as changed. This updates the UI for committing
* the value * the value
@ -417,12 +414,19 @@ namespace geode {
*/ */
virtual void onCommit() = 0; virtual void onCommit() = 0;
void onDescription(CCObject*);
void onReset(CCObject*);
public: public:
void commit(); void commit();
virtual bool hasUncommittedChanges() const = 0; virtual bool hasUncommittedChanges() const = 0;
virtual bool hasNonDefaultValue() const = 0; virtual bool hasNonDefaultValue() const = 0;
virtual void resetToDefault() = 0; virtual void resetToDefault() = 0;
cocos2d::CCLabelBMFont* getNameLabel() const;
cocos2d::CCMenu* getNameMenu() const;
cocos2d::CCMenu* getButtonMenu() const;
void setContentSize(cocos2d::CCSize const& size) override; void setContentSize(cocos2d::CCSize const& size) override;
std::shared_ptr<SettingV3> getSetting() const; std::shared_ptr<SettingV3> getSetting() const;

View file

@ -318,6 +318,10 @@ namespace geode {
template <class T> template <class T>
std::optional<T> tryGet() { std::optional<T> tryGet() {
if (this->hasError()) return std::nullopt; if (this->hasError()) return std::nullopt;
if constexpr (std::is_same_v<T, matjson::Value>) {
return this->getJSONRef();
}
else {
try { try {
return this->getJSONRef().template as<T>(); return this->getJSONRef().template as<T>();
} }
@ -325,6 +329,7 @@ namespace geode {
catch(std::exception const& e) { catch(std::exception const& e) {
this->setError("invalid json type: {}", e); this->setError("invalid json type: {}", e);
} }
}
return std::nullopt; return std::nullopt;
} }

View file

@ -64,13 +64,6 @@
} }
}, },
"settings": { "settings": {
"show-platform-console": {
"type": "bool",
"default": false,
"name": "Show Platform Console",
"description": "Show the native console (if one exists). <cr>This setting is meant for developers</c>",
"platforms": ["win", "mac"]
},
"auto-check-updates": { "auto-check-updates": {
"type": "bool", "type": "bool",
"default": true, "default": true,
@ -83,6 +76,24 @@
"name": "Disable Crash Popup", "name": "Disable Crash Popup",
"description": "Disables the popup at startup asking if you'd like to send a bug report; intended for developers" "description": "Disables the popup at startup asking if you'd like to send a bug report; intended for developers"
}, },
"enable-geode-theme": {
"type": "bool",
"default": true,
"name": "Enable Geode-Themed Colors",
"description": "When enabled, the Geode menu has a <ca>Geode-themed color scheme</c>. <cy>This does not affect any other menus!</c>"
},
"developer-title": {
"type": "title",
"name": "Developer Settings"
},
"show-platform-console": {
"type": "bool",
"default": false,
"name": "Show Platform Console",
"description": "Show the native console (if one exists). <cr>This setting is meant for developers</c>",
"platforms": ["win", "mac"],
"restart-required": true
},
"server-cache-size-limit": { "server-cache-size-limit": {
"type": "int", "type": "int",
"default": 20, "default": 20,
@ -90,12 +101,6 @@
"max": 100, "max": 100,
"name": "Server Cache Size Limit", "name": "Server Cache Size Limit",
"description": "Limits the size of the cache used for loading mods. Higher values result in higher memory usage." "description": "Limits the size of the cache used for loading mods. Higher values result in higher memory usage."
},
"enable-geode-theme": {
"type": "bool",
"default": true,
"name": "Enable Geode-Themed Colors",
"description": "When enabled, the Geode menu has a <ca>Geode-themed color scheme</c>. <cy>This does not affect any other menus!</c>"
} }
}, },
"issues": { "issues": {

View file

@ -173,8 +173,8 @@ void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<Sett
log::error("Unable to register custom setting: {}", reg.unwrapErr()); log::error("Unable to register custom setting: {}", reg.unwrapErr());
} }
} }
Result<> Mod::registerCustomSettingV3(std::string_view const key, std::shared_ptr<SettingV3> value) { Result<> Mod::registerCustomSettingType(std::string_view type, SettingGenerator generator) {
return m_impl->m_settings->registerCustomSetting(key, value); return m_impl->m_settings->registerCustomSettingType(type, generator);
} }
std::vector<std::string> Mod::getLaunchArgumentNames() const { std::vector<std::string> Mod::getLaunchArgumentNames() const {

View file

@ -4,7 +4,7 @@
#include "ModPatch.hpp" #include "ModPatch.hpp"
#include <Geode/loader/Loader.hpp> #include <Geode/loader/Loader.hpp>
#include <string_view> #include <string_view>
#include "ModSettingsManager.hpp" #include <Geode/loader/ModSettingsManager.hpp>
namespace geode { namespace geode {
class Mod::Impl { class Mod::Impl {

View file

@ -1,63 +1,161 @@
#include "ModSettingsManager.hpp" #include <Geode/loader/ModSettingsManager.hpp>
#include "SettingV3Impl.hpp"
#include <Geode/utils/JsonValidation.hpp> #include <Geode/utils/JsonValidation.hpp>
#include "ModImpl.hpp"
using namespace geode::prelude;
// All setting type generators are put in a shared pool for two reasons:
// #1 no need to duplicate the built-in settings between all mods
// #2 easier lookup of custom settings if a mod uses another mod's custom setting type
class SharedSettingTypesPool final {
private:
std::unordered_map<std::string, SettingGenerator> m_types;
SharedSettingTypesPool() : m_types({
// todo in v4: remove this
{ "custom", &LegacyCustomSettingV3::parse },
{ "title", &TitleSettingV3::parse },
{ "bool", &BoolSettingV3::parse },
{ "int", &IntSettingV3::parse },
{ "float", &FloatSettingV3::parse },
{ "string", &StringSettingV3::parse },
{ "file", &FileSettingV3::parse },
{ "path", &FileSettingV3::parse },
{ "rgb", &Color3BSettingV3::parse },
{ "color", &Color3BSettingV3::parse },
{ "rgba", &Color4BSettingV3::parse },
}) {}
public:
static SharedSettingTypesPool& get() {
static auto inst = SharedSettingTypesPool();
return inst;
}
Result<> add(std::string_view modID, std::string_view type, SettingGenerator generator) {
// Limit type to just [a-z0-9\-]+
if (type.empty() || !std::all_of(type.begin(), type.end(), +[](char c) {
return
('a' <= c && c <= 'z') ||
('0' <= c && c <= '9') ||
(c == '-');
})) {
return Err("Custom setting types must match the regex [a-z0-9\\-]+");
}
auto full = fmt::format("{}/{}", modID, type);
if (m_types.contains(full)) {
return Err("Type \"{}\" has already been registered for mod {}", type, modID);
}
m_types.emplace(full, generator);
return Ok();
}
std::optional<SettingGenerator> find(std::string_view modID, std::string_view fullType) {
auto full = std::string(
fullType.starts_with("custom:") ?
fullType.substr(fullType.find(':') + 1) :
fullType
);
if (!full.find('/')) {
full = fmt::format("{}/{}", modID, full);
}
if (m_types.contains(full)) {
return m_types.at(full);
}
return std::nullopt;
}
};
class ModSettingsManager::Impl final { class ModSettingsManager::Impl final {
public: public:
struct SettingInfo final { struct SettingInfo final {
std::shared_ptr<SettingV3> v3; std::string type;
matjson::Value json;
std::shared_ptr<SettingV3> v3 = nullptr;
// todo: remove in v4 // todo: remove in v4
std::shared_ptr<SettingValue> legacy = nullptr; std::shared_ptr<SettingValue> legacy = nullptr;
}; };
std::string modID; std::string modID;
std::unordered_map<std::string, SettingInfo> list; std::unordered_map<std::string, SettingInfo> settings;
void createSettings() {
for (auto& [key, setting] : settings) {
if (setting.v3) {
continue;
}
auto gen = SharedSettingTypesPool::get().find(modID, setting.type);
// The type was not found, meaning it probably hasn't been registered yet
if (!gen) {
continue;
}
if (auto v3 = (*gen)(key, modID, setting.json)) {
setting.v3 = *v3;
}
else {
log::error(
"Unable to parse setting '{}' for mod {}: {}",
key, modID, v3.unwrapErr()
);
}
}
}
}; };
ModSettingsManager* ModSettingsManager::from(Mod* mod) {
return ModImpl::getImpl(mod)->m_settings.get();
}
ModSettingsManager::ModSettingsManager(ModMetadata const& metadata) ModSettingsManager::ModSettingsManager(ModMetadata const& metadata)
: m_impl(std::make_unique<Impl>()) : m_impl(std::make_unique<Impl>())
{ {
m_impl->modID = metadata.getID(); m_impl->modID = metadata.getID();
for (auto const& [key, json] : metadata.getSettingsV3()) { for (auto const& [key, json] : metadata.getSettingsV3()) {
if (auto v3 = SettingV3::parseBuiltin(key, m_impl->modID, json)) {
auto setting = Impl::SettingInfo(); auto setting = Impl::SettingInfo();
setting.v3.swap(*v3); setting.json = json;
m_impl->list.emplace(key, setting); auto root = checkJson(json, "setting");
root.needs("type").into(setting.type);
if (root) {
if (setting.type == "custom") {
log::warn(
"Setting \"{}\" in mod {} has the old \"custom\" type - "
"this type has been deprecated and will be removed in Geode v4.0.0. "
"Use the new \"custom:type-name-here\" syntax for defining custom "
"setting types - see more in INSERT TUTORIAL HERE",
key, m_impl->modID
);
}
m_impl->settings.emplace(key, setting);
} }
else { else {
log::error("Unable to parse setting '{}' for mod {}: {}", key, m_impl->modID, v3.unwrapErr()); log::error("Setting '{}' in mod {} is missing type", key, m_impl->modID);
} }
} }
m_impl->createSettings();
} }
ModSettingsManager::~ModSettingsManager() {} ModSettingsManager::~ModSettingsManager() {}
ModSettingsManager::ModSettingsManager(ModSettingsManager&&) = default;
Result<> ModSettingsManager::registerCustomSetting(std::string_view key, std::shared_ptr<SettingV3> ptr) { Result<> ModSettingsManager::registerCustomSettingType(std::string_view type, SettingGenerator generator) {
if (!ptr) { GEODE_UNWRAP(SharedSettingTypesPool::get().add(m_impl->modID, type, generator));
return Err("Custom settings must not be null!"); m_impl->createSettings();
}
auto id = std::string(key);
if (!m_impl->list.count(id)) {
return Err("No such setting '{}' in mod {}", id, m_impl->modID);
}
auto& sett = m_impl->list.at(id);
sett.v3.swap(ptr);
return Ok(); return Ok();
} }
Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr) { Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr) {
auto id = std::string(key); auto id = std::string(key);
if (!m_impl->list.count(id)) { if (!m_impl->settings.count(id)) {
return Err("No such setting '{}' in mod {}", id, m_impl->modID); return Err("No such setting '{}' in mod {}", id, m_impl->modID);
} }
auto& sett = m_impl->list.at(id); auto& sett = m_impl->settings.at(id);
if (auto custom = typeinfo_pointer_cast<UnresolvedCustomSettingV3>(sett.v3)) { if (auto custom = typeinfo_pointer_cast<LegacyCustomSettingV3>(sett.v3)) {
if (!custom->m_impl->legacyValue) { if (!custom->getValue()) {
custom->m_impl->legacyValue = std::move(ptr); custom->setValue(std::move(ptr));
} }
else { else {
return Err("Setting '{}' in mod {} has already been registed", id, m_impl->modID); return Err("Setting '{}' in mod {} has already been registed", id, m_impl->modID);
} }
} }
else { else {
return Err("Setting '{}' in mod {} is not a custom setting", id, m_impl->modID); return Err("Setting '{}' in mod {} is not a legacy custom setting", id, m_impl->modID);
} }
return Ok(); return Ok();
} }
@ -65,9 +163,9 @@ Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, s
Result<> ModSettingsManager::load(matjson::Value const& json) { Result<> ModSettingsManager::load(matjson::Value const& json) {
auto root = checkJson(json, "Settings"); auto root = checkJson(json, "Settings");
for (auto const& [key, value] : root.properties()) { for (auto const& [key, value] : root.properties()) {
if (m_impl->list.contains(key)) { if (m_impl->settings.contains(key)) {
try { try {
if (!m_impl->list.at(key).v3->load(value.json())) { if (!m_impl->settings.at(key).v3->load(value.json())) {
log::error("Unable to load setting '{}' for mod {}", key, m_impl->modID); log::error("Unable to load setting '{}' for mod {}", key, m_impl->modID);
} }
} }
@ -79,7 +177,7 @@ Result<> ModSettingsManager::load(matjson::Value const& json) {
return Ok(); return Ok();
} }
void ModSettingsManager::save(matjson::Value& json) { void ModSettingsManager::save(matjson::Value& json) {
for (auto& [key, sett] : m_impl->list) { for (auto& [key, sett] : m_impl->settings) {
// Store the value in an intermediary so if `save` fails the existing // Store the value in an intermediary so if `save` fails the existing
// value loaded from disk isn't overwritten // value loaded from disk isn't overwritten
matjson::Value value; matjson::Value value;
@ -99,18 +197,22 @@ void ModSettingsManager::save(matjson::Value& json) {
std::shared_ptr<SettingV3> ModSettingsManager::get(std::string_view key) { std::shared_ptr<SettingV3> ModSettingsManager::get(std::string_view key) {
auto id = std::string(key); auto id = std::string(key);
return m_impl->list.count(id) ? m_impl->list.at(id).v3 : nullptr; return m_impl->settings.count(id) ? m_impl->settings.at(id).v3 : nullptr;
} }
std::shared_ptr<SettingValue> ModSettingsManager::getLegacy(std::string_view key) { std::shared_ptr<SettingValue> ModSettingsManager::getLegacy(std::string_view key) {
auto id = std::string(key); auto id = std::string(key);
if (!m_impl->list.count(id)) { if (!m_impl->settings.count(id)) {
return nullptr; return nullptr;
} }
auto& info = m_impl->list.at(id); auto& info = m_impl->settings.at(id);
// If this setting has alreay been given a legacy interface, give that // If this setting has alreay been given a legacy interface, give that
if (info.legacy) { if (info.legacy) {
return info.legacy; return info.legacy;
} }
// Uninitialized settings are null
if (!info.v3) {
return nullptr;
}
// Generate new legacy interface // Generate new legacy interface
if (auto legacy = info.v3->convertToLegacyValue()) { if (auto legacy = info.v3->convertToLegacyValue()) {
info.legacy.swap(*legacy); info.legacy.swap(*legacy);

View file

@ -1,26 +0,0 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include <Geode/loader/SettingV3.hpp>
using namespace geode::prelude;
class ModSettingsManager final {
private:
class Impl;
std::unique_ptr<Impl> m_impl;
public:
ModSettingsManager(ModMetadata const& metadata);
~ModSettingsManager();
Result<> load(matjson::Value const& json);
void save(matjson::Value& json);
Result<> registerCustomSetting(std::string_view key, std::shared_ptr<SettingV3> ptr);
Result<> registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr);
std::shared_ptr<SettingV3> get(std::string_view key);
std::shared_ptr<SettingValue> getLegacy(std::string_view key);
std::optional<Setting> getLegacyDefinition(std::string_view key);
};

View file

@ -1,5 +1,4 @@
#include "SettingNodeV3.hpp" #include "SettingNodeV3.hpp"
#include "SettingV3Impl.hpp"
#include <Geode/loader/SettingNode.hpp> #include <Geode/loader/SettingNode.hpp>
class SettingNodeSizeChangeEventV3::Impl final { class SettingNodeSizeChangeEventV3::Impl final {
@ -37,6 +36,10 @@ bool SettingNodeValueChangeEventV3::isCommit() const {
class SettingNodeV3::Impl final { class SettingNodeV3::Impl final {
public: public:
std::shared_ptr<SettingV3> setting; std::shared_ptr<SettingV3> setting;
CCLabelBMFont* nameLabel;
CCMenu* nameMenu;
CCMenu* buttonMenu;
CCMenuItemSpriteExtra* resetButton;
}; };
bool SettingNodeV3::init(std::shared_ptr<SettingV3> setting, float width) { bool SettingNodeV3::init(std::shared_ptr<SettingV3> setting, float width) {
@ -46,23 +49,93 @@ bool SettingNodeV3::init(std::shared_ptr<SettingV3> setting, float width) {
m_impl = std::make_shared<Impl>(); m_impl = std::make_shared<Impl>();
m_impl->setting = setting; m_impl->setting = setting;
m_impl->nameMenu = CCMenu::create();
m_impl->nameMenu->setContentWidth(width / 2 - 20);
m_impl->nameLabel = CCLabelBMFont::create(
setting->getName().value_or(setting->getKey()).c_str(),
"bigFont.fnt"
);
m_impl->nameLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .4f)->setScalePriority(1));
m_impl->nameMenu->addChild(m_impl->nameLabel);
if (setting->getDescription()) {
auto descSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png");
descSpr->setScale(.5f);
auto descBtn = CCMenuItemSpriteExtra::create(
descSpr, this, menu_selector(SettingNodeV3::onDescription)
);
m_impl->nameMenu->addChild(descBtn);
}
auto resetSpr = CCSprite::createWithSpriteFrameName("reset-gold.png"_spr);
resetSpr->setScale(.5f);
m_impl->resetButton = CCMenuItemSpriteExtra::create(
resetSpr, this, menu_selector(SettingNodeV3::onReset)
);
m_impl->nameMenu->addChild(m_impl->resetButton);
m_impl->nameMenu->setLayout(RowLayout::create()->setAxisAlignment(AxisAlignment::Start));
this->addChildAtPosition(m_impl->nameMenu, Anchor::Left, ccp(10, 0), ccp(0, .5f));
m_impl->buttonMenu = CCMenu::create();
m_impl->buttonMenu->setContentWidth(width / 2 - 20);
m_impl->buttonMenu->setLayout(RowLayout::create()->setAxisAlignment(AxisAlignment::End));
this->addChildAtPosition(m_impl->buttonMenu, Anchor::Right, ccp(-10, 0), ccp(1, .5f));
this->setAnchorPoint({ .5f, .5f });
this->setContentSize({ width, 30 });
return true; return true;
} }
void SettingNodeV3::markChanged() { void SettingNodeV3::updateState() {
SettingNodeValueChangeEventV3(false).post(); this->getNameLabel()->setColor(this->hasUncommittedChanges() ? ccc3(17, 221, 0) : ccWHITE);
m_impl->resetButton->setVisible(this->hasNonDefaultValue());
m_impl->nameMenu->updateLayout();
} }
void SettingNodeV3::onDescription(CCObject*) {
auto title = m_impl->setting->getName().value_or(m_impl->setting->getKey());
FLAlertLayer::create(
nullptr,
title.c_str(),
m_impl->setting->getDescription().value_or("No description provided"),
"OK", nullptr,
clamp(title.size() * 16, 240, 400)
)->show();
}
void SettingNodeV3::onReset(CCObject*) {
this->resetToDefault();
this->updateState();
}
void SettingNodeV3::markChanged() {
this->updateState();
SettingNodeValueChangeEventV3(false).post();
}
void SettingNodeV3::commit() { void SettingNodeV3::commit() {
this->onCommit(); this->onCommit();
this->updateState();
SettingNodeValueChangeEventV3(true).post(); SettingNodeValueChangeEventV3(true).post();
} }
void SettingNodeV3::setContentSize(CCSize const& size) { void SettingNodeV3::setContentSize(CCSize const& size) {
CCNode::setContentSize(size); CCNode::setContentSize(size);
this->updateLayout();
SettingNodeSizeChangeEventV3(this).post(); SettingNodeSizeChangeEventV3(this).post();
} }
CCLabelBMFont* SettingNodeV3::getNameLabel() const {
return m_impl->nameLabel;
}
CCMenu* SettingNodeV3::getNameMenu() const {
return m_impl->nameMenu;
}
CCMenu* SettingNodeV3::getButtonMenu() const {
return m_impl->buttonMenu;
}
std::shared_ptr<SettingV3> SettingNodeV3::getSetting() const { std::shared_ptr<SettingV3> SettingNodeV3::getSetting() const {
return m_impl->setting; return m_impl->setting;
} }
@ -73,11 +146,10 @@ bool TitleSettingNodeV3::init(std::shared_ptr<TitleSettingV3> setting, float wid
if (!SettingNodeV3::init(setting, width)) if (!SettingNodeV3::init(setting, width))
return false; return false;
this->getNameLabel()->setFntFile("goldFont.fnt");
this->getNameMenu()->updateLayout();
this->setContentHeight(20); this->setContentHeight(20);
this->updateState();
auto label = CCLabelBMFont::create(setting->getTitle().c_str(), "goldFont.fnt");
label->limitLabelWidth(width - m_obContentSize.height, .7f, .1f);
this->addChildAtPosition(label, Anchor::Left, ccp(m_obContentSize.height / 2, 0));
return true; return true;
} }
@ -112,16 +184,19 @@ bool BoolSettingNodeV3::init(std::shared_ptr<BoolSettingV3> setting, float width
if (!SettingNodeV3::init(setting, width)) if (!SettingNodeV3::init(setting, width))
return false; return false;
this->setContentHeight(30);
auto label = CCLabelBMFont::create(setting->getName().c_str(), "bigFont.fnt");
label->limitLabelWidth(width - m_obContentSize.height * 1.5f, .5f, .1f);
this->addChildAtPosition(label, Anchor::Left, ccp(m_obContentSize.height / 2, 0));
m_toggle = CCMenuItemToggler::createWithStandardSprites( m_toggle = CCMenuItemToggler::createWithStandardSprites(
this, nullptr, .8f this, menu_selector(BoolSettingNodeV3::onToggle), .55f
); );
this->addChildAtPosition(m_toggle, Anchor::Right, ccp(-m_obContentSize.height / 2, 0)); m_toggle->m_onButton->setContentSize({ 25, 25 });
m_toggle->m_onButton->getNormalImage()->setPosition(ccp(25, 25) / 2);
m_toggle->m_offButton->setContentSize({ 25, 25 });
m_toggle->m_offButton->getNormalImage()->setPosition(ccp(25, 25) / 2);
m_toggle->m_notClickable = true;
m_toggle->toggle(setting->getValue());
this->getButtonMenu()->addChild(m_toggle);
this->getButtonMenu()->updateLayout();
this->updateState();
return true; return true;
} }
@ -129,6 +204,10 @@ bool BoolSettingNodeV3::init(std::shared_ptr<BoolSettingV3> setting, float width
void BoolSettingNodeV3::onCommit() { void BoolSettingNodeV3::onCommit() {
this->getSetting()->setValue(m_toggle->isToggled()); this->getSetting()->setValue(m_toggle->isToggled());
} }
void BoolSettingNodeV3::onToggle(CCObject*) {
m_toggle->toggle(!m_toggle->isToggled());
this->markChanged();
}
BoolSettingNodeV3* BoolSettingNodeV3::create(std::shared_ptr<BoolSettingV3> setting, float width) { BoolSettingNodeV3* BoolSettingNodeV3::create(std::shared_ptr<BoolSettingV3> setting, float width) {
auto ret = new BoolSettingNodeV3(); auto ret = new BoolSettingNodeV3();
@ -367,7 +446,7 @@ std::shared_ptr<Color4BSettingV3> Color4BSettingNodeV3::getSetting() const {
// UnresolvedCustomSettingNodeV3 // UnresolvedCustomSettingNodeV3
bool UnresolvedCustomSettingNodeV3::init(std::shared_ptr<UnresolvedCustomSettingV3> setting, float width) { bool UnresolvedCustomSettingNodeV3::init(std::shared_ptr<LegacyCustomSettingV3> setting, float width) {
if (!SettingNodeV3::init(setting, width)) if (!SettingNodeV3::init(setting, width))
return false; return false;
@ -385,7 +464,7 @@ bool UnresolvedCustomSettingNodeV3::init(std::shared_ptr<UnresolvedCustomSetting
void UnresolvedCustomSettingNodeV3::onCommit() {} void UnresolvedCustomSettingNodeV3::onCommit() {}
UnresolvedCustomSettingNodeV3* UnresolvedCustomSettingNodeV3::create(std::shared_ptr<UnresolvedCustomSettingV3> setting, float width) { UnresolvedCustomSettingNodeV3* UnresolvedCustomSettingNodeV3::create(std::shared_ptr<LegacyCustomSettingV3> setting, float width) {
auto ret = new UnresolvedCustomSettingNodeV3(); auto ret = new UnresolvedCustomSettingNodeV3();
if (ret && ret->init(setting, width)) { if (ret && ret->init(setting, width)) {
ret->autorelease(); ret->autorelease();
@ -403,17 +482,17 @@ bool UnresolvedCustomSettingNodeV3::hasNonDefaultValue() const {
} }
void UnresolvedCustomSettingNodeV3::resetToDefault() {} void UnresolvedCustomSettingNodeV3::resetToDefault() {}
std::shared_ptr<UnresolvedCustomSettingV3> UnresolvedCustomSettingNodeV3::getSetting() const { std::shared_ptr<LegacyCustomSettingV3> UnresolvedCustomSettingNodeV3::getSetting() const {
return std::static_pointer_cast<UnresolvedCustomSettingV3>(SettingNodeV3::getSetting()); return std::static_pointer_cast<LegacyCustomSettingV3>(SettingNodeV3::getSetting());
} }
// LegacyCustomSettingToV3Node // LegacyCustomSettingToV3Node
bool LegacyCustomSettingToV3Node::init(std::shared_ptr<UnresolvedCustomSettingV3> original, float width) { bool LegacyCustomSettingToV3Node::init(std::shared_ptr<LegacyCustomSettingV3> original, float width) {
if (!SettingNodeV3::init(original, width)) if (!SettingNodeV3::init(original, width))
return false; return false;
m_original = original->m_impl->legacyValue->createNode(width); m_original = original->getValue()->createNode(width);
this->setContentSize({ width, m_original->getContentHeight() }); this->setContentSize({ width, m_original->getContentHeight() });
this->addChildAtPosition(m_original, Anchor::Center); this->addChildAtPosition(m_original, Anchor::Center);
@ -424,7 +503,7 @@ void LegacyCustomSettingToV3Node::onCommit() {
m_original->commit(); m_original->commit();
} }
LegacyCustomSettingToV3Node* LegacyCustomSettingToV3Node::create(std::shared_ptr<UnresolvedCustomSettingV3> original, float width) { LegacyCustomSettingToV3Node* LegacyCustomSettingToV3Node::create(std::shared_ptr<LegacyCustomSettingV3> original, float width) {
auto ret = new LegacyCustomSettingToV3Node(); auto ret = new LegacyCustomSettingToV3Node();
if (ret && ret->init(original, width)) { if (ret && ret->init(original, width)) {
ret->autorelease(); ret->autorelease();

View file

@ -33,6 +33,8 @@ protected:
void onCommit() override; void onCommit() override;
void onToggle(CCObject*);
public: public:
static BoolSettingNodeV3* create(std::shared_ptr<BoolSettingV3> setting, float width); static BoolSettingNodeV3* create(std::shared_ptr<BoolSettingV3> setting, float width);
@ -141,18 +143,18 @@ public:
class UnresolvedCustomSettingNodeV3 : public SettingNodeV3 { class UnresolvedCustomSettingNodeV3 : public SettingNodeV3 {
protected: protected:
bool init(std::shared_ptr<UnresolvedCustomSettingV3> setting, float width); bool init(std::shared_ptr<LegacyCustomSettingV3> setting, float width);
void onCommit() override; void onCommit() override;
public: public:
static UnresolvedCustomSettingNodeV3* create(std::shared_ptr<UnresolvedCustomSettingV3> setting, float width); static UnresolvedCustomSettingNodeV3* create(std::shared_ptr<LegacyCustomSettingV3> setting, float width);
bool hasUncommittedChanges() const override; bool hasUncommittedChanges() const override;
bool hasNonDefaultValue() const override; bool hasNonDefaultValue() const override;
void resetToDefault() override; void resetToDefault() override;
std::shared_ptr<UnresolvedCustomSettingV3> getSetting() const; std::shared_ptr<LegacyCustomSettingV3> getSetting() const;
}; };
// If these classes do get exposed in headers, this SHOULD NOT BE EXPOSED!!!!!! DO NOT DO THAT!!!! // If these classes do get exposed in headers, this SHOULD NOT BE EXPOSED!!!!!! DO NOT DO THAT!!!!
@ -161,12 +163,12 @@ class LegacyCustomSettingToV3Node : public SettingNodeV3 {
protected: protected:
SettingNode* m_original; SettingNode* m_original;
bool init(std::shared_ptr<UnresolvedCustomSettingV3> original, float width); bool init(std::shared_ptr<LegacyCustomSettingV3> original, float width);
void onCommit() override; void onCommit() override;
public: public:
static LegacyCustomSettingToV3Node* create(std::shared_ptr<UnresolvedCustomSettingV3> original, float width); static LegacyCustomSettingToV3Node* create(std::shared_ptr<LegacyCustomSettingV3> original, float width);
bool hasUncommittedChanges() const override; bool hasUncommittedChanges() const override;
bool hasNonDefaultValue() const override; bool hasNonDefaultValue() const override;

View file

@ -1,7 +1,6 @@
#include <Geode/loader/SettingV3.hpp> #include <Geode/loader/SettingV3.hpp>
#include <Geode/utils/JsonValidation.hpp> #include <Geode/utils/JsonValidation.hpp>
#include <regex> #include <regex>
#include "SettingV3Impl.hpp"
#include "SettingNodeV3.hpp" #include "SettingNodeV3.hpp"
using namespace geode::prelude; using namespace geode::prelude;
@ -10,11 +9,34 @@ class SettingV3::GeodeImpl {
public: public:
std::string modID; std::string modID;
std::string key; std::string key;
std::optional<std::string> name;
std::optional<std::string> description;
std::optional<std::string> enableIf;
bool requiresRestart = false;
}; };
SettingV3::SettingV3() : m_impl(std::make_shared<GeodeImpl>()) {}
SettingV3::~SettingV3() = default; SettingV3::~SettingV3() = default;
SettingV3::SettingV3() : m_impl(std::make_shared<GeodeImpl>()) {} Result<> SettingV3::parseSharedProperties(std::string const& key, std::string const& modID, matjson::Value const& value, bool onlyNameAndDesc) {
auto json = checkJson(value, "SettingV3");
this->parseSharedProperties(key, modID, json, onlyNameAndDesc);
return json.ok();
}
void SettingV3::parseSharedProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc) {
this->init(key, modID);
value.needs("type");
value.has("name").into(m_impl->name);
value.has("description").into(m_impl->description);
if (!onlyNameAndDesc) {
value.has("enable-if").into(m_impl->enableIf);
value.has("requires-restart").into(m_impl->requiresRestart);
}
}
void SettingV3::init(std::string const& key, std::string const& modID) {
m_impl->key = key;
m_impl->modID = modID;
}
std::string SettingV3::getKey() const { std::string SettingV3::getKey() const {
return m_impl->key; return m_impl->key;
@ -22,37 +44,22 @@ std::string SettingV3::getKey() const {
std::string SettingV3::getModID() const { std::string SettingV3::getModID() const {
return m_impl->modID; return m_impl->modID;
} }
std::optional<std::string> SettingV3::getName() const {
return m_impl->name;
}
std::optional<std::string> SettingV3::getDescription() const {
return m_impl->description;
}
std::optional<std::string> SettingV3::getEnableIf() const {
return m_impl->enableIf;
}
bool SettingV3::requiresRestart() const {
return m_impl->requiresRestart;
}
Mod* SettingV3::getMod() const { Mod* SettingV3::getMod() const {
return Loader::get()->getInstalledMod(m_impl->modID); return Loader::get()->getInstalledMod(m_impl->modID);
} }
Result<> SettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
m_impl->key = key;
m_impl->modID = modID;
return this->onParse(key, modID, json);
}
Result<std::shared_ptr<SettingV3>> SettingV3::parseBuiltin(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "SettingV3");
std::string type;
root.needs("type").into(type);
std::shared_ptr<SettingV3> ret;
switch (hash(type)) {
case hash("bool"): ret = std::make_shared<BoolSettingV3>(BoolSettingV3::PrivateMarker()); break;
case hash("int"): ret = std::make_shared<IntSettingV3>(IntSettingV3::PrivateMarker()); break;
case hash("float"): ret = std::make_shared<FloatSettingV3>(FloatSettingV3::PrivateMarker()); break;
case hash("string"): ret = std::make_shared<StringSettingV3>(StringSettingV3::PrivateMarker()); break;
case hash("rgb"): case hash("color"): ret = std::make_shared<Color3BSettingV3>(Color3BSettingV3::PrivateMarker()); break;
case hash("rgba"): ret = std::make_shared<Color4BSettingV3>(Color4BSettingV3::PrivateMarker()); break;
case hash("path"): case hash("file"): ret = std::make_shared<FileSettingV3>(FileSettingV3::PrivateMarker()); break;
case hash("title"): ret = std::make_shared<TitleSettingV3>(TitleSettingV3::PrivateMarker()); break;
default:
case hash("custom"): ret = std::make_shared<UnresolvedCustomSettingV3>(UnresolvedCustomSettingV3::PrivateMarker()); break;
}
GEODE_UNWRAP(ret->parse(key, modID, json));
return root.ok(std::move(ret));
}
std::optional<Setting> SettingV3::convertToLegacy() const { std::optional<Setting> SettingV3::convertToLegacy() const {
return std::nullopt; return std::nullopt;
} }
@ -60,58 +67,20 @@ std::optional<std::shared_ptr<SettingValue>> SettingV3::convertToLegacyValue() c
return std::nullopt; return std::nullopt;
} }
class geode::detail::GeodeSettingBaseV3::Impl final {
public:
std::optional<std::string> name;
std::optional<std::string> description;
std::optional<std::string> enableIf;
};
geode::detail::GeodeSettingBaseV3::GeodeSettingBaseV3() : m_impl(std::make_shared<Impl>()) {}
std::string geode::detail::GeodeSettingBaseV3::getName() const {
return m_impl->name.value_or(this->getKey());
}
std::optional<std::string> geode::detail::GeodeSettingBaseV3::getDescription() const {
return m_impl->description;
}
std::optional<std::string> geode::detail::GeodeSettingBaseV3::getEnableIf() const {
return m_impl->enableIf;
}
Result<> geode::detail::GeodeSettingBaseV3::parseSharedBase(JsonExpectedValue& json) {
// Mark keys that have been checked before-hand
json.needs("type");
json.has("platforms");
json.has("name").into(m_impl->name);
json.has("description").into(m_impl->description);
json.has("enable-if").into(m_impl->enableIf);
return Ok();
}
Result<> geode::detail::GeodeSettingBaseV3::isValidShared() const {
// In the future if something like `enable-if` preventing
// programmatic modification of settings it should be added here
return Ok();
}
class TitleSettingV3::Impl final { class TitleSettingV3::Impl final {
public: public:
std::string title;
}; };
TitleSettingV3::TitleSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} TitleSettingV3::TitleSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
std::string TitleSettingV3::getTitle() const { Result<std::shared_ptr<TitleSettingV3>> TitleSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
return m_impl->title; auto ret = std::make_shared<TitleSettingV3>(PrivateMarker());
auto root = checkJson(json, "TitleSettingV3");
ret->parseSharedProperties(key, modID, root, true);
root.checkUnknownKeys();
return root.ok(ret);
} }
Result<> TitleSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "TitleSettingV3");
root.needs("title").into(m_impl->title);
root.checkUnknownKeys();
return root.ok();
}
bool TitleSettingV3::load(matjson::Value const& json) { bool TitleSettingV3::load(matjson::Value const& json) {
return true; return true;
} }
@ -128,46 +97,56 @@ bool TitleSettingV3::isDefaultValue() const {
} }
void TitleSettingV3::reset() {} void TitleSettingV3::reset() {}
// todo in v4: move the UnresolvedCustomSettingV3::Impl definition from SettingV3Impl.hpp to here class LegacyCustomSettingV3::Impl final {
// on this line in particular public:
// right here matjson::Value json;
// replace this comment with it std::shared_ptr<SettingValue> legacyValue = nullptr;
// put it riiiiiight here };
UnresolvedCustomSettingV3::UnresolvedCustomSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} LegacyCustomSettingV3::LegacyCustomSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
Result<> UnresolvedCustomSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { Result<std::shared_ptr<LegacyCustomSettingV3>> LegacyCustomSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
m_impl->json = json; auto ret = std::make_shared<LegacyCustomSettingV3>(PrivateMarker());
return Ok(); ret->init(key, modID);
ret->m_impl->json = json;
return Ok(ret);
} }
bool UnresolvedCustomSettingV3::load(matjson::Value const& json) {
std::shared_ptr<SettingValue> LegacyCustomSettingV3::getValue() const {
return m_impl->legacyValue;
}
void LegacyCustomSettingV3::setValue(std::shared_ptr<SettingValue> value) {
m_impl->legacyValue = value;
}
bool LegacyCustomSettingV3::load(matjson::Value const& json) {
return true; return true;
} }
bool UnresolvedCustomSettingV3::save(matjson::Value& json) const { bool LegacyCustomSettingV3::save(matjson::Value& json) const {
return true; return true;
} }
SettingNodeV3* UnresolvedCustomSettingV3::createNode(float width) { SettingNodeV3* LegacyCustomSettingV3::createNode(float width) {
if (m_impl->legacyValue) { if (m_impl->legacyValue) {
return LegacyCustomSettingToV3Node::create( return LegacyCustomSettingToV3Node::create(
std::static_pointer_cast<UnresolvedCustomSettingV3>(shared_from_this()), width std::static_pointer_cast<LegacyCustomSettingV3>(shared_from_this()), width
); );
} }
return UnresolvedCustomSettingNodeV3::create( return UnresolvedCustomSettingNodeV3::create(
std::static_pointer_cast<UnresolvedCustomSettingV3>(shared_from_this()), width std::static_pointer_cast<LegacyCustomSettingV3>(shared_from_this()), width
); );
} }
bool UnresolvedCustomSettingV3::isDefaultValue() const { bool LegacyCustomSettingV3::isDefaultValue() const {
return true; return true;
} }
void UnresolvedCustomSettingV3::reset() {} void LegacyCustomSettingV3::reset() {}
std::optional<Setting> UnresolvedCustomSettingV3::convertToLegacy() const { std::optional<Setting> LegacyCustomSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(CustomSetting { return Setting(this->getKey(), this->getModID(), SettingKind(CustomSetting {
.json = std::make_shared<ModJson>(m_impl->json) .json = std::make_shared<ModJson>(m_impl->json)
})); }));
} }
std::optional<std::shared_ptr<SettingValue>> UnresolvedCustomSettingV3::convertToLegacyValue() const { std::optional<std::shared_ptr<SettingValue>> LegacyCustomSettingV3::convertToLegacyValue() const {
return m_impl->legacyValue ? std::optional(m_impl->legacyValue) : std::nullopt; return m_impl->legacyValue ? std::optional(m_impl->legacyValue) : std::nullopt;
} }
@ -179,6 +158,18 @@ public:
BoolSettingV3::BoolSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} BoolSettingV3::BoolSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
Result<std::shared_ptr<BoolSettingV3>> BoolSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<BoolSettingV3>(PrivateMarker());
auto root = checkJson(json, "BoolSettingV3");
ret->parseSharedProperties(key, modID, root);
ret->parseDefaultValue(root, ret->m_impl->defaultValue);
ret->m_impl->value = ret->m_impl->defaultValue;
root.checkUnknownKeys();
return root.ok(ret);
}
bool& BoolSettingV3::getValueMut() const { bool& BoolSettingV3::getValueMut() const {
return m_impl->value; return m_impl->value;
} }
@ -186,19 +177,9 @@ bool BoolSettingV3::getDefaultValue() const {
return m_impl->defaultValue; return m_impl->defaultValue;
} }
Result<> BoolSettingV3::isValid(bool value) const { Result<> BoolSettingV3::isValid(bool value) const {
GEODE_UNWRAP(this->isValidShared());
return Ok(); return Ok();
} }
Result<> BoolSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "BoolSettingV3");
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
m_impl->value = m_impl->defaultValue;
root.checkUnknownKeys();
return root.ok();
}
bool BoolSettingV3::load(matjson::Value const& json) { bool BoolSettingV3::load(matjson::Value const& json) {
if (json.is_bool()) { if (json.is_bool()) {
m_impl->value = json.as_bool(); m_impl->value = json.as_bool();
@ -244,6 +225,35 @@ public:
} controls; } controls;
}; };
Result<std::shared_ptr<IntSettingV3>> IntSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<IntSettingV3>(PrivateMarker());
auto root = checkJson(json, "IntSettingV3");
ret->parseSharedProperties(key, modID, root);
ret->parseDefaultValue(root, ret->m_impl->defaultValue);
ret->m_impl->value = ret->m_impl->defaultValue;
root.has("min").into(ret->m_impl->minValue);
root.has("max").into(ret->m_impl->maxValue);
if (auto controls = root.has("control")) {
controls.has("arrow-step").into(ret->m_impl->controls.arrowStepSize);
if (!controls.has("arrows").template get<bool>()) {
ret->m_impl->controls.arrowStepSize = 0;
}
controls.has("big-arrow-step").into(ret->m_impl->controls.bigArrowStepSize);
if (!controls.has("big-arrows").template get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
controls.has("slider").into(ret->m_impl->controls.sliderEnabled);
controls.has("slider-step").into(ret->m_impl->controls.sliderSnap);
controls.has("input").into(ret->m_impl->controls.textInputEnabled);
controls.checkUnknownKeys();
}
root.checkUnknownKeys();
return root.ok(ret);
}
IntSettingV3::IntSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} IntSettingV3::IntSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
int64_t& IntSettingV3::getValueMut() const { int64_t& IntSettingV3::getValueMut() const {
@ -253,7 +263,6 @@ int64_t IntSettingV3::getDefaultValue() const {
return m_impl->defaultValue; return m_impl->defaultValue;
} }
Result<> IntSettingV3::isValid(int64_t value) const { Result<> IntSettingV3::isValid(int64_t value) const {
GEODE_UNWRAP(this->isValidShared());
if (m_impl->minValue && value < *m_impl->minValue) { if (m_impl->minValue && value < *m_impl->minValue) {
return Err("value must be at least {}", *m_impl->minValue); return Err("value must be at least {}", *m_impl->minValue);
} }
@ -292,32 +301,6 @@ bool IntSettingV3::isInputEnabled() const {
return m_impl->controls.textInputEnabled; return m_impl->controls.textInputEnabled;
} }
Result<> IntSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "IntSettingV3");
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
m_impl->value = m_impl->defaultValue;
root.has("min").into(m_impl->minValue);
root.has("max").into(m_impl->maxValue);
if (auto controls = root.has("control")) {
controls.has("arrow-step").into(m_impl->controls.arrowStepSize);
if (!controls.has("arrows").template get<bool>()) {
m_impl->controls.arrowStepSize = 0;
}
controls.has("big-arrow-step").into(m_impl->controls.bigArrowStepSize);
if (!controls.has("big-arrows").template get<bool>()) {
m_impl->controls.bigArrowStepSize = 0;
}
controls.has("slider").into(m_impl->controls.sliderEnabled);
controls.has("slider-step").into(m_impl->controls.sliderSnap);
controls.has("input").into(m_impl->controls.textInputEnabled);
controls.checkUnknownKeys();
}
root.checkUnknownKeys();
return root.ok();
}
bool IntSettingV3::load(matjson::Value const& json) { bool IntSettingV3::load(matjson::Value const& json) {
if (json.is_number()) { if (json.is_number()) {
m_impl->value = json.as_int(); m_impl->value = json.as_int();
@ -376,6 +359,35 @@ public:
FloatSettingV3::FloatSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} FloatSettingV3::FloatSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
Result<std::shared_ptr<FloatSettingV3>> FloatSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<FloatSettingV3>(PrivateMarker());
auto root = checkJson(json, "FloatSettingV3");
ret->parseSharedProperties(key, modID, root);
ret->parseDefaultValue(root, ret->m_impl->defaultValue);
ret->m_impl->value = ret->m_impl->defaultValue;
root.has("min").into(ret->m_impl->minValue);
root.has("max").into(ret->m_impl->maxValue);
if (auto controls = root.has("control")) {
controls.has("arrow-step").into(ret->m_impl->controls.arrowStepSize);
if (!controls.has("arrows").template get<bool>()) {
ret->m_impl->controls.arrowStepSize = 0;
}
controls.has("big-arrow-step").into(ret->m_impl->controls.bigArrowStepSize);
if (!controls.has("big-arrows").template get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
controls.has("slider").into(ret->m_impl->controls.sliderEnabled);
controls.has("slider-step").into(ret->m_impl->controls.sliderSnap);
controls.has("input").into(ret->m_impl->controls.textInputEnabled);
controls.checkUnknownKeys();
}
root.checkUnknownKeys();
return root.ok(ret);
}
double& FloatSettingV3::getValueMut() const { double& FloatSettingV3::getValueMut() const {
return m_impl->value; return m_impl->value;
} }
@ -383,7 +395,6 @@ double FloatSettingV3::getDefaultValue() const {
return m_impl->defaultValue; return m_impl->defaultValue;
} }
Result<> FloatSettingV3::isValid(double value) const { Result<> FloatSettingV3::isValid(double value) const {
GEODE_UNWRAP(this->isValidShared());
if (m_impl->minValue && value < *m_impl->minValue) { if (m_impl->minValue && value < *m_impl->minValue) {
return Err("value must be at least {}", *m_impl->minValue); return Err("value must be at least {}", *m_impl->minValue);
} }
@ -422,32 +433,6 @@ bool FloatSettingV3::isInputEnabled() const {
return m_impl->controls.textInputEnabled; return m_impl->controls.textInputEnabled;
} }
Result<> FloatSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "FloatSettingV3");
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
m_impl->value = m_impl->defaultValue;
root.has("min").into(m_impl->minValue);
root.has("max").into(m_impl->maxValue);
if (auto controls = root.has("control")) {
controls.has("arrow-step").into(m_impl->controls.arrowStepSize);
if (!controls.has("arrows").template get<bool>()) {
m_impl->controls.arrowStepSize = 0;
}
controls.has("big-arrow-step").into(m_impl->controls.bigArrowStepSize);
if (!controls.has("big-arrows").template get<bool>()) {
m_impl->controls.bigArrowStepSize = 0;
}
controls.has("slider").into(m_impl->controls.sliderEnabled);
controls.has("slider-step").into(m_impl->controls.sliderSnap);
controls.has("input").into(m_impl->controls.textInputEnabled);
controls.checkUnknownKeys();
}
root.checkUnknownKeys();
return root.ok();
}
bool FloatSettingV3::load(matjson::Value const& json) { bool FloatSettingV3::load(matjson::Value const& json) {
if (json.is_number()) { if (json.is_number()) {
m_impl->value = json.as_double(); m_impl->value = json.as_double();
@ -498,6 +483,22 @@ public:
StringSettingV3::StringSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} StringSettingV3::StringSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
Result<std::shared_ptr<StringSettingV3>> StringSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<StringSettingV3>(PrivateMarker());
auto root = checkJson(json, "StringSettingV3");
ret->parseSharedProperties(key, modID, root);
ret->parseDefaultValue(root, ret->m_impl->defaultValue);
ret->m_impl->value = ret->m_impl->defaultValue;
root.has("match").into(ret->m_impl->match);
root.has("filter").into(ret->m_impl->filter);
root.has("one-of").into(ret->m_impl->oneOf);
root.checkUnknownKeys();
return root.ok(ret);
}
std::string& StringSettingV3::getValueMut() const { std::string& StringSettingV3::getValueMut() const {
return m_impl->value; return m_impl->value;
} }
@ -505,7 +506,6 @@ std::string StringSettingV3::getDefaultValue() const {
return m_impl->defaultValue; return m_impl->defaultValue;
} }
Result<> StringSettingV3::isValid(std::string_view value) const { Result<> StringSettingV3::isValid(std::string_view value) const {
GEODE_UNWRAP(this->isValidShared());
if (m_impl->match) { if (m_impl->match) {
if (!std::regex_match(std::string(value), std::regex(*m_impl->match))) { if (!std::regex_match(std::string(value), std::regex(*m_impl->match))) {
return Err("value must match regex {}", *m_impl->match); return Err("value must match regex {}", *m_impl->match);
@ -529,19 +529,6 @@ std::optional<std::vector<std::string>> StringSettingV3::getEnumOptions() const
return m_impl->oneOf; return m_impl->oneOf;
} }
Result<> StringSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "StringSettingV3");
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
m_impl->value = m_impl->defaultValue;
root.has("match").into(m_impl->match);
root.has("filter").into(m_impl->filter);
root.has("one-of").into(m_impl->oneOf);
root.checkUnknownKeys();
return root.ok();
}
bool StringSettingV3::load(matjson::Value const& json) { bool StringSettingV3::load(matjson::Value const& json) {
if (json.is_string()) { if (json.is_string()) {
m_impl->value = json.as_string(); m_impl->value = json.as_string();
@ -582,30 +569,18 @@ public:
FileSettingV3::FileSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} FileSettingV3::FileSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
std::filesystem::path& FileSettingV3::getValueMut() const { Result<std::shared_ptr<FileSettingV3>> FileSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
return m_impl->value; auto ret = std::make_shared<FileSettingV3>(PrivateMarker());
}
std::filesystem::path FileSettingV3::getDefaultValue() const {
return m_impl->defaultValue;
}
Result<> FileSettingV3::isValid(std::filesystem::path const& value) const {
GEODE_UNWRAP(this->isValidShared());
return Ok();
}
std::optional<std::vector<utils::file::FilePickOptions::Filter>> FileSettingV3::getFilters() const {
return m_impl->filters;
}
Result<> FileSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "FileSettingV3"); auto root = checkJson(json, "FileSettingV3");
ret->parseSharedProperties(key, modID, root);
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); ret->parseDefaultValue(root, ret->m_impl->defaultValue);
ret->m_impl->value = ret->m_impl->defaultValue;
// Replace known paths like `{gd-save-dir}/` // Replace known paths like `{gd-save-dir}/`
try { try {
m_impl->defaultValue = fmt::format( ret->m_impl->defaultValue = fmt::format(
fmt::runtime(m_impl->defaultValue.string()), fmt::runtime(ret->m_impl->defaultValue.string()),
fmt::arg("gd-save-dir", dirs::getSaveDir()), fmt::arg("gd-save-dir", dirs::getSaveDir()),
fmt::arg("gd-game-dir", dirs::getGameDir()), fmt::arg("gd-game-dir", dirs::getGameDir()),
fmt::arg("mod-config-dir", dirs::getModConfigDir() / modID), fmt::arg("mod-config-dir", dirs::getModConfigDir() / modID),
@ -616,7 +591,7 @@ Result<> FileSettingV3::onParse(std::string const& key, std::string const& modID
catch(fmt::format_error const&) { catch(fmt::format_error const&) {
return Err("Invalid format string for file setting path"); return Err("Invalid format string for file setting path");
} }
m_impl->value = m_impl->defaultValue; ret->m_impl->value = ret->m_impl->defaultValue;
if (auto controls = root.has("control")) { if (auto controls = root.has("control")) {
auto filters = std::vector<file::FilePickOptions::Filter>(); auto filters = std::vector<file::FilePickOptions::Filter>();
@ -627,13 +602,28 @@ Result<> FileSettingV3::onParse(std::string const& key, std::string const& modID
filters.push_back(filter); filters.push_back(filter);
} }
if (!filters.empty()) { if (!filters.empty()) {
m_impl->filters.emplace(filters); ret->m_impl->filters.emplace(filters);
} }
} }
root.checkUnknownKeys(); root.checkUnknownKeys();
return root.ok(); return root.ok(ret);
} }
std::filesystem::path& FileSettingV3::getValueMut() const {
return m_impl->value;
}
std::filesystem::path FileSettingV3::getDefaultValue() const {
return m_impl->defaultValue;
}
Result<> FileSettingV3::isValid(std::filesystem::path const& value) const {
return Ok();
}
std::optional<std::vector<utils::file::FilePickOptions::Filter>> FileSettingV3::getFilters() const {
return m_impl->filters;
}
bool FileSettingV3::load(matjson::Value const& json) { bool FileSettingV3::load(matjson::Value const& json) {
if (json.is_string()) { if (json.is_string()) {
m_impl->value = json.as_string(); m_impl->value = json.as_string();
@ -671,6 +661,18 @@ public:
Color3BSettingV3::Color3BSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} Color3BSettingV3::Color3BSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
Result<std::shared_ptr<Color3BSettingV3>> Color3BSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<Color3BSettingV3>(PrivateMarker());
auto root = checkJson(json, "Color3BSettingV3");
ret->parseSharedProperties(key, modID, root);
ret->parseDefaultValue(root, ret->m_impl->defaultValue);
ret->m_impl->value = ret->m_impl->defaultValue;
root.checkUnknownKeys();
return root.ok(ret);
}
ccColor3B& Color3BSettingV3::getValueMut() const { ccColor3B& Color3BSettingV3::getValueMut() const {
return m_impl->value; return m_impl->value;
} }
@ -678,19 +680,9 @@ ccColor3B Color3BSettingV3::getDefaultValue() const {
return m_impl->defaultValue; return m_impl->defaultValue;
} }
Result<> Color3BSettingV3::isValid(ccColor3B value) const { Result<> Color3BSettingV3::isValid(ccColor3B value) const {
GEODE_UNWRAP(this->isValidShared());
return Ok(); return Ok();
} }
Result<> Color3BSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "Color3BSettingV3");
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
m_impl->value = m_impl->defaultValue;
root.checkUnknownKeys();
return root.ok();
}
bool Color3BSettingV3::load(matjson::Value const& json) { bool Color3BSettingV3::load(matjson::Value const& json) {
if (json.template is<ccColor3B>()) { if (json.template is<ccColor3B>()) {
m_impl->value = json.template as<ccColor3B>(); m_impl->value = json.template as<ccColor3B>();
@ -727,6 +719,18 @@ public:
Color4BSettingV3::Color4BSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {} Color4BSettingV3::Color4BSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
Result<std::shared_ptr<Color4BSettingV3>> Color4BSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<Color4BSettingV3>(PrivateMarker());
auto root = checkJson(json, "Color4BSettingV3");
ret->parseSharedProperties(key, modID, root);
ret->parseDefaultValue(root, ret->m_impl->defaultValue);
ret->m_impl->value = ret->m_impl->defaultValue;
root.checkUnknownKeys();
return root.ok(ret);
}
ccColor4B& Color4BSettingV3::getValueMut() const { ccColor4B& Color4BSettingV3::getValueMut() const {
return m_impl->value; return m_impl->value;
} }
@ -734,19 +738,9 @@ ccColor4B Color4BSettingV3::getDefaultValue() const {
return m_impl->defaultValue; return m_impl->defaultValue;
} }
Result<> Color4BSettingV3::isValid(ccColor4B value) const { Result<> Color4BSettingV3::isValid(ccColor4B value) const {
GEODE_UNWRAP(this->isValidShared());
return Ok(); return Ok();
} }
Result<> Color4BSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "Color4BSettingV3");
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
m_impl->value = m_impl->defaultValue;
root.checkUnknownKeys();
return root.ok();
}
bool Color4BSettingV3::load(matjson::Value const& json) { bool Color4BSettingV3::load(matjson::Value const& json) {
if (json.template is<ccColor4B>()) { if (json.template is<ccColor4B>()) {
m_impl->value = json.template as<ccColor4B>(); m_impl->value = json.template as<ccColor4B>();

View file

@ -1,18 +0,0 @@
#pragma once
#include <Geode/loader/SettingV3.hpp>
using namespace geode::prelude;
// todo in v4: this header can be fully removed and the impl moved back into SettingV3.cpp
// for now it has to be exposed for ModSettingsManager legacy compatibility
class UnresolvedCustomSettingV3::Impl final {
public:
matjson::Value json;
// todo: remove in v4
// this is for compatability with legacy custom settings
// in v3 settings custom settings just replace the definition fully like a normal person
std::shared_ptr<SettingValue> legacyValue = nullptr;
};

View file

@ -28,7 +28,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
hasBG = !hasBG; hasBG = !hasBG;
auto bg = CCLayerColor::create({ 0, 0, 0, 50 }); auto bg = CCLayerColor::create({ 0, 0, 0, 50 });
bg->setOpacity(hasBG ? 50 : 0); bg->setOpacity(hasBG ? 60 : 20);
SettingNodeV3* node; SettingNodeV3* node;
if (auto sett = mod->getSettingV3(key)) { if (auto sett = mod->getSettingV3(key)) {
@ -40,11 +40,12 @@ bool ModSettingsPopup::setup(Mod* mod) {
} }
bg->setContentSize(node->getScaledContentSize()); bg->setContentSize(node->getScaledContentSize());
bg->addChildAtPosition(node, Anchor::Center); bg->addChildAtPosition(node, Anchor::Center, ccp(0, 0), ccp(.5f, .5f));
auto separator = CCLayerColor::create({ 0, 0, 0, 50 }, layerSize.width, 1.f); // auto separator = CCLayerColor::create({ 0, 0, 0, 50 }, layerSize.width, 1.f);
separator->setOpacity(hasBG ? 100 : 50); // separator->setOpacity(hasBG ? 100 : 50);
bg->addChildAtPosition(separator, Anchor::Bottom); // separator->ignoreAnchorPointForPosition(false);
// bg->addChildAtPosition(separator, Anchor::Bottom, ccp(0, 0), ccp(.5f, .5f));
m_settings.push_back(node); m_settings.push_back(node);
@ -132,11 +133,13 @@ void ModSettingsPopup::onResetAll(CCObject*) {
void ModSettingsPopup::updateState() { void ModSettingsPopup::updateState() {
if (this->hasUncommitted()) { if (this->hasUncommitted()) {
m_applyBtnSpr->setColor({0xff, 0xff, 0xff}); m_applyBtnSpr->setColor(ccWHITE);
m_applyBtnSpr->setOpacity(255);
m_applyBtn->setEnabled(true); m_applyBtn->setEnabled(true);
} }
else { else {
m_applyBtnSpr->setColor({0x44, 0x44, 0x44}); m_applyBtnSpr->setColor(ccGRAY);
m_applyBtnSpr->setOpacity(155);
m_applyBtn->setEnabled(false); m_applyBtn->setEnabled(false);
} }
} }
@ -172,7 +175,7 @@ void ModSettingsPopup::onOpenSaveDirectory(CCObject*) {
ModSettingsPopup* ModSettingsPopup::create(Mod* mod) { ModSettingsPopup* ModSettingsPopup::create(Mod* mod) {
auto ret = new ModSettingsPopup(); auto ret = new ModSettingsPopup();
if (ret->init(440.f, 280.f, mod)) { if (ret->init(440, 280, mod)) {
ret->autorelease(); ret->autorelease();
return ret; return ret;
} }