mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-23 07:57:51 -05:00
Compare commits
3 commits
71eb4a2041
...
5263483b0a
Author | SHA1 | Date | |
---|---|---|---|
|
5263483b0a | ||
|
40a28eec7b | ||
|
a5f56cb7cb |
14 changed files with 596 additions and 400 deletions
|
@ -191,11 +191,6 @@ namespace geode {
|
|||
* @see addCustomSetting
|
||||
*/
|
||||
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
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom setting type
|
||||
*/
|
||||
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
|
||||
|
||||
/**
|
||||
* Returns a prefixed launch argument name. See `Mod::getLaunchArgument`
|
||||
* for details about mod-specific launch arguments.
|
||||
|
|
46
loader/include/Geode/loader/ModSettingsManager.hpp
Normal file
46
loader/include/Geode/loader/ModSettingsManager.hpp
Normal 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);
|
||||
};
|
||||
}
|
|
@ -9,22 +9,22 @@
|
|||
// this unfortunately has to be included because of C++ templates
|
||||
#include "../utils/JsonValidation.hpp"
|
||||
|
||||
// todo in v4: these can be removed as well as the friend decl in UnresolvedCustomSettingV3
|
||||
class ModSettingsManager;
|
||||
// todo in v4: these can be removed as well as the friend decl in LegacyCustomSettingV3
|
||||
class LegacyCustomSettingToV3Node;
|
||||
|
||||
namespace geode {
|
||||
class ModSettingsManager;
|
||||
class SettingNodeV3;
|
||||
|
||||
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
|
||||
private:
|
||||
class GeodeImpl;
|
||||
std::shared_ptr<GeodeImpl> m_impl;
|
||||
|
||||
|
||||
protected:
|
||||
virtual Result<> onParse(
|
||||
std::string const& key, std::string const& modID, matjson::Value const& json
|
||||
) = 0;
|
||||
void init(std::string const& key, std::string const& modID);
|
||||
Result<> parseSharedProperties(std::string const& key, std::string const& modID, matjson::Value const& value, bool onlyNameAndDesc = false);
|
||||
void parseSharedProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc = false);
|
||||
|
||||
public:
|
||||
SettingV3();
|
||||
|
@ -43,8 +43,23 @@ namespace geode {
|
|||
* while the mod is still being initialized
|
||||
*/
|
||||
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 save(matjson::Value& json) const = 0;
|
||||
virtual SettingNodeV3* createNode(float width) = 0;
|
||||
|
@ -59,26 +74,22 @@ namespace geode {
|
|||
virtual std::optional<Setting> convertToLegacy() const;
|
||||
[[deprecated("This function will be removed alongside legacy settings in 4.0.0!")]]
|
||||
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 {
|
||||
class GEODE_DLL GeodeSettingBaseV3 : public SettingV3 {
|
||||
private:
|
||||
class Impl;
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
Result<> parseSharedBase(JsonExpectedValue& json);
|
||||
|
||||
template <class T, class V = T>
|
||||
class GeodeSettingBaseValueV3 : public SettingV3 {
|
||||
protected:
|
||||
Result<> isValidShared() const;
|
||||
virtual T& getValueMut() const = 0;
|
||||
|
||||
template <class T>
|
||||
Result<> parseShared(JsonExpectedValue& json, T& defaultValue) {
|
||||
GEODE_UNWRAP(this->parseSharedBase(json));
|
||||
void parseDefaultValue(JsonExpectedValue& json, T& defaultValue) {
|
||||
auto value = json.needs("default");
|
||||
// Check if this is a platform-specific default value
|
||||
if (value.isObject() && value.has(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)) {
|
||||
|
@ -87,22 +98,8 @@ namespace geode {
|
|||
else {
|
||||
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:
|
||||
using ValueType = T;
|
||||
|
||||
|
@ -130,17 +127,13 @@ namespace geode {
|
|||
class Impl;
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
protected:
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
friend class SettingV3;
|
||||
|
||||
public:
|
||||
TitleSettingV3(PrivateMarker);
|
||||
|
||||
std::string getTitle() const;
|
||||
static Result<std::shared_ptr<TitleSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
|
||||
|
||||
bool load(matjson::Value const& json) override;
|
||||
bool save(matjson::Value& json) const override;
|
||||
|
@ -150,23 +143,25 @@ namespace geode {
|
|||
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:
|
||||
class Impl;
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
friend class ::ModSettingsManager;
|
||||
friend class ::geode::ModSettingsManager;
|
||||
friend class ::LegacyCustomSettingToV3Node;
|
||||
|
||||
protected:
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
friend class SettingV3;
|
||||
|
||||
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 save(matjson::Value& json) const override;
|
||||
|
@ -186,7 +181,6 @@ namespace geode {
|
|||
|
||||
protected:
|
||||
bool& getValueMut() const override;
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
|
@ -194,6 +188,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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;
|
||||
Result<> isValid(bool value) const override;
|
||||
|
@ -213,7 +208,6 @@ namespace geode {
|
|||
|
||||
protected:
|
||||
int64_t& getValueMut() const override;
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
|
@ -221,6 +215,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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;
|
||||
Result<> isValid(int64_t value) const override;
|
||||
|
@ -251,7 +246,6 @@ namespace geode {
|
|||
|
||||
protected:
|
||||
double& getValueMut() const override;
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
|
@ -259,6 +253,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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;
|
||||
Result<> isValid(double value) const override;
|
||||
|
@ -289,7 +284,6 @@ namespace geode {
|
|||
|
||||
protected:
|
||||
std::string& getValueMut() const override;
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
|
@ -297,6 +291,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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;
|
||||
Result<> isValid(std::string_view value) const override;
|
||||
|
@ -320,7 +315,6 @@ namespace geode {
|
|||
|
||||
protected:
|
||||
std::filesystem::path& getValueMut() const override;
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
|
@ -328,6 +322,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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;
|
||||
Result<> isValid(std::filesystem::path const& value) const override;
|
||||
|
@ -349,7 +344,6 @@ namespace geode {
|
|||
|
||||
protected:
|
||||
cocos2d::ccColor3B& getValueMut() const override;
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
|
@ -357,6 +351,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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;
|
||||
Result<> isValid(cocos2d::ccColor3B value) const override;
|
||||
|
@ -376,7 +371,6 @@ namespace geode {
|
|||
|
||||
protected:
|
||||
cocos2d::ccColor4B& getValueMut() const override;
|
||||
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
|
@ -384,6 +378,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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;
|
||||
Result<> isValid(cocos2d::ccColor4B value) const override;
|
||||
|
@ -404,6 +399,8 @@ namespace geode {
|
|||
protected:
|
||||
bool init(std::shared_ptr<SettingV3> setting, float width);
|
||||
|
||||
virtual void updateState();
|
||||
|
||||
/**
|
||||
* Mark this setting as changed. This updates the UI for committing
|
||||
* the value
|
||||
|
@ -417,12 +414,19 @@ namespace geode {
|
|||
*/
|
||||
virtual void onCommit() = 0;
|
||||
|
||||
void onDescription(CCObject*);
|
||||
void onReset(CCObject*);
|
||||
|
||||
public:
|
||||
void commit();
|
||||
virtual bool hasUncommittedChanges() const = 0;
|
||||
virtual bool hasNonDefaultValue() const = 0;
|
||||
virtual void resetToDefault() = 0;
|
||||
|
||||
cocos2d::CCLabelBMFont* getNameLabel() const;
|
||||
cocos2d::CCMenu* getNameMenu() const;
|
||||
cocos2d::CCMenu* getButtonMenu() const;
|
||||
|
||||
void setContentSize(cocos2d::CCSize const& size) override;
|
||||
|
||||
std::shared_ptr<SettingV3> getSetting() const;
|
||||
|
|
|
@ -318,12 +318,17 @@ namespace geode {
|
|||
template <class T>
|
||||
std::optional<T> tryGet() {
|
||||
if (this->hasError()) return std::nullopt;
|
||||
try {
|
||||
return this->getJSONRef().template as<T>();
|
||||
if constexpr (std::is_same_v<T, matjson::Value>) {
|
||||
return this->getJSONRef();
|
||||
}
|
||||
// matjson can throw variant exceptions too so you need to do this
|
||||
catch(std::exception const& e) {
|
||||
this->setError("invalid json type: {}", e);
|
||||
else {
|
||||
try {
|
||||
return this->getJSONRef().template as<T>();
|
||||
}
|
||||
// matjson can throw variant exceptions too so you need to do this
|
||||
catch(std::exception const& e) {
|
||||
this->setError("invalid json type: {}", e);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
|
@ -64,13 +64,6 @@
|
|||
}
|
||||
},
|
||||
"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": {
|
||||
"type": "bool",
|
||||
"default": true,
|
||||
|
@ -83,6 +76,24 @@
|
|||
"name": "Disable Crash Popup",
|
||||
"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": {
|
||||
"type": "int",
|
||||
"default": 20,
|
||||
|
@ -90,12 +101,6 @@
|
|||
"max": 100,
|
||||
"name": "Server Cache Size Limit",
|
||||
"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": {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
Result<> Mod::registerCustomSettingV3(std::string_view const key, std::shared_ptr<SettingV3> value) {
|
||||
return m_impl->m_settings->registerCustomSetting(key, value);
|
||||
Result<> Mod::registerCustomSettingType(std::string_view type, SettingGenerator generator) {
|
||||
return m_impl->m_settings->registerCustomSettingType(type, generator);
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::getLaunchArgumentNames() const {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "ModPatch.hpp"
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <string_view>
|
||||
#include "ModSettingsManager.hpp"
|
||||
#include <Geode/loader/ModSettingsManager.hpp>
|
||||
|
||||
namespace geode {
|
||||
class Mod::Impl {
|
||||
|
|
|
@ -1,63 +1,161 @@
|
|||
#include "ModSettingsManager.hpp"
|
||||
#include "SettingV3Impl.hpp"
|
||||
#include <Geode/loader/ModSettingsManager.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 {
|
||||
public:
|
||||
struct SettingInfo final {
|
||||
std::shared_ptr<SettingV3> v3;
|
||||
std::string type;
|
||||
matjson::Value json;
|
||||
std::shared_ptr<SettingV3> v3 = nullptr;
|
||||
// todo: remove in v4
|
||||
std::shared_ptr<SettingValue> legacy = nullptr;
|
||||
};
|
||||
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)
|
||||
: m_impl(std::make_unique<Impl>())
|
||||
{
|
||||
m_impl->modID = metadata.getID();
|
||||
for (auto const& [key, json] : metadata.getSettingsV3()) {
|
||||
if (auto v3 = SettingV3::parseBuiltin(key, m_impl->modID, json)) {
|
||||
auto setting = Impl::SettingInfo();
|
||||
setting.v3.swap(*v3);
|
||||
m_impl->list.emplace(key, setting);
|
||||
auto setting = Impl::SettingInfo();
|
||||
setting.json = json;
|
||||
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 {
|
||||
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&&) = default;
|
||||
|
||||
Result<> ModSettingsManager::registerCustomSetting(std::string_view key, std::shared_ptr<SettingV3> ptr) {
|
||||
if (!ptr) {
|
||||
return Err("Custom settings must not be null!");
|
||||
}
|
||||
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);
|
||||
Result<> ModSettingsManager::registerCustomSettingType(std::string_view type, SettingGenerator generator) {
|
||||
GEODE_UNWRAP(SharedSettingTypesPool::get().add(m_impl->modID, type, generator));
|
||||
m_impl->createSettings();
|
||||
return Ok();
|
||||
}
|
||||
Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr) {
|
||||
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);
|
||||
}
|
||||
auto& sett = m_impl->list.at(id);
|
||||
if (auto custom = typeinfo_pointer_cast<UnresolvedCustomSettingV3>(sett.v3)) {
|
||||
if (!custom->m_impl->legacyValue) {
|
||||
custom->m_impl->legacyValue = std::move(ptr);
|
||||
auto& sett = m_impl->settings.at(id);
|
||||
if (auto custom = typeinfo_pointer_cast<LegacyCustomSettingV3>(sett.v3)) {
|
||||
if (!custom->getValue()) {
|
||||
custom->setValue(std::move(ptr));
|
||||
}
|
||||
else {
|
||||
return Err("Setting '{}' in mod {} has already been registed", id, m_impl->modID);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
@ -65,9 +163,9 @@ Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, s
|
|||
Result<> ModSettingsManager::load(matjson::Value const& json) {
|
||||
auto root = checkJson(json, "Settings");
|
||||
for (auto const& [key, value] : root.properties()) {
|
||||
if (m_impl->list.contains(key)) {
|
||||
if (m_impl->settings.contains(key)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +177,7 @@ Result<> ModSettingsManager::load(matjson::Value const& json) {
|
|||
return Ok();
|
||||
}
|
||||
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
|
||||
// value loaded from disk isn't overwritten
|
||||
matjson::Value value;
|
||||
|
@ -99,18 +197,22 @@ void ModSettingsManager::save(matjson::Value& json) {
|
|||
|
||||
std::shared_ptr<SettingV3> ModSettingsManager::get(std::string_view 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) {
|
||||
auto id = std::string(key);
|
||||
if (!m_impl->list.count(id)) {
|
||||
if (!m_impl->settings.count(id)) {
|
||||
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 (info.legacy) {
|
||||
return info.legacy;
|
||||
}
|
||||
// Uninitialized settings are null
|
||||
if (!info.v3) {
|
||||
return nullptr;
|
||||
}
|
||||
// Generate new legacy interface
|
||||
if (auto legacy = info.v3->convertToLegacyValue()) {
|
||||
info.legacy.swap(*legacy);
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -1,5 +1,4 @@
|
|||
#include "SettingNodeV3.hpp"
|
||||
#include "SettingV3Impl.hpp"
|
||||
#include <Geode/loader/SettingNode.hpp>
|
||||
|
||||
class SettingNodeSizeChangeEventV3::Impl final {
|
||||
|
@ -37,6 +36,10 @@ bool SettingNodeValueChangeEventV3::isCommit() const {
|
|||
class SettingNodeV3::Impl final {
|
||||
public:
|
||||
std::shared_ptr<SettingV3> setting;
|
||||
CCLabelBMFont* nameLabel;
|
||||
CCMenu* nameMenu;
|
||||
CCMenu* buttonMenu;
|
||||
CCMenuItemSpriteExtra* resetButton;
|
||||
};
|
||||
|
||||
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->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;
|
||||
}
|
||||
|
||||
void SettingNodeV3::markChanged() {
|
||||
SettingNodeValueChangeEventV3(false).post();
|
||||
void SettingNodeV3::updateState() {
|
||||
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() {
|
||||
this->onCommit();
|
||||
this->updateState();
|
||||
SettingNodeValueChangeEventV3(true).post();
|
||||
}
|
||||
|
||||
void SettingNodeV3::setContentSize(CCSize const& size) {
|
||||
CCNode::setContentSize(size);
|
||||
this->updateLayout();
|
||||
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 {
|
||||
return m_impl->setting;
|
||||
}
|
||||
|
@ -73,11 +146,10 @@ bool TitleSettingNodeV3::init(std::shared_ptr<TitleSettingV3> setting, float wid
|
|||
if (!SettingNodeV3::init(setting, width))
|
||||
return false;
|
||||
|
||||
this->getNameLabel()->setFntFile("goldFont.fnt");
|
||||
this->getNameMenu()->updateLayout();
|
||||
this->setContentHeight(20);
|
||||
|
||||
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));
|
||||
this->updateState();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -112,16 +184,19 @@ bool BoolSettingNodeV3::init(std::shared_ptr<BoolSettingV3> setting, float width
|
|||
if (!SettingNodeV3::init(setting, width))
|
||||
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(
|
||||
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;
|
||||
}
|
||||
|
@ -129,6 +204,10 @@ bool BoolSettingNodeV3::init(std::shared_ptr<BoolSettingV3> setting, float width
|
|||
void BoolSettingNodeV3::onCommit() {
|
||||
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) {
|
||||
auto ret = new BoolSettingNodeV3();
|
||||
|
@ -367,7 +446,7 @@ std::shared_ptr<Color4BSettingV3> Color4BSettingNodeV3::getSetting() const {
|
|||
|
||||
// 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))
|
||||
return false;
|
||||
|
||||
|
@ -385,7 +464,7 @@ bool UnresolvedCustomSettingNodeV3::init(std::shared_ptr<UnresolvedCustomSetting
|
|||
|
||||
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();
|
||||
if (ret && ret->init(setting, width)) {
|
||||
ret->autorelease();
|
||||
|
@ -403,17 +482,17 @@ bool UnresolvedCustomSettingNodeV3::hasNonDefaultValue() const {
|
|||
}
|
||||
void UnresolvedCustomSettingNodeV3::resetToDefault() {}
|
||||
|
||||
std::shared_ptr<UnresolvedCustomSettingV3> UnresolvedCustomSettingNodeV3::getSetting() const {
|
||||
return std::static_pointer_cast<UnresolvedCustomSettingV3>(SettingNodeV3::getSetting());
|
||||
std::shared_ptr<LegacyCustomSettingV3> UnresolvedCustomSettingNodeV3::getSetting() const {
|
||||
return std::static_pointer_cast<LegacyCustomSettingV3>(SettingNodeV3::getSetting());
|
||||
}
|
||||
|
||||
// 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))
|
||||
return false;
|
||||
|
||||
m_original = original->m_impl->legacyValue->createNode(width);
|
||||
m_original = original->getValue()->createNode(width);
|
||||
this->setContentSize({ width, m_original->getContentHeight() });
|
||||
this->addChildAtPosition(m_original, Anchor::Center);
|
||||
|
||||
|
@ -424,7 +503,7 @@ void LegacyCustomSettingToV3Node::onCommit() {
|
|||
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();
|
||||
if (ret && ret->init(original, width)) {
|
||||
ret->autorelease();
|
||||
|
|
|
@ -33,6 +33,8 @@ protected:
|
|||
|
||||
void onCommit() override;
|
||||
|
||||
void onToggle(CCObject*);
|
||||
|
||||
public:
|
||||
static BoolSettingNodeV3* create(std::shared_ptr<BoolSettingV3> setting, float width);
|
||||
|
||||
|
@ -141,18 +143,18 @@ public:
|
|||
|
||||
class UnresolvedCustomSettingNodeV3 : public SettingNodeV3 {
|
||||
protected:
|
||||
bool init(std::shared_ptr<UnresolvedCustomSettingV3> setting, float width);
|
||||
bool init(std::shared_ptr<LegacyCustomSettingV3> setting, float width);
|
||||
|
||||
void onCommit() override;
|
||||
|
||||
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 hasNonDefaultValue() const 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!!!!
|
||||
|
@ -161,12 +163,12 @@ class LegacyCustomSettingToV3Node : public SettingNodeV3 {
|
|||
protected:
|
||||
SettingNode* m_original;
|
||||
|
||||
bool init(std::shared_ptr<UnresolvedCustomSettingV3> original, float width);
|
||||
bool init(std::shared_ptr<LegacyCustomSettingV3> original, float width);
|
||||
|
||||
void onCommit() override;
|
||||
|
||||
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 hasNonDefaultValue() const override;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <regex>
|
||||
#include "SettingV3Impl.hpp"
|
||||
#include "SettingNodeV3.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
@ -10,11 +9,34 @@ class SettingV3::GeodeImpl {
|
|||
public:
|
||||
std::string modID;
|
||||
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() : 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 {
|
||||
return m_impl->key;
|
||||
|
@ -22,37 +44,22 @@ std::string SettingV3::getKey() const {
|
|||
std::string SettingV3::getModID() const {
|
||||
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 {
|
||||
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 {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -60,58 +67,20 @@ std::optional<std::shared_ptr<SettingValue>> SettingV3::convertToLegacyValue() c
|
|||
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 {
|
||||
public:
|
||||
std::string title;
|
||||
};
|
||||
|
||||
TitleSettingV3::TitleSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
|
||||
|
||||
std::string TitleSettingV3::getTitle() const {
|
||||
return m_impl->title;
|
||||
Result<std::shared_ptr<TitleSettingV3>> TitleSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
|
@ -128,46 +97,56 @@ bool TitleSettingV3::isDefaultValue() const {
|
|||
}
|
||||
void TitleSettingV3::reset() {}
|
||||
|
||||
// todo in v4: move the UnresolvedCustomSettingV3::Impl definition from SettingV3Impl.hpp to here
|
||||
// on this line in particular
|
||||
// right here
|
||||
// replace this comment with it
|
||||
// put it riiiiiight here
|
||||
class LegacyCustomSettingV3::Impl final {
|
||||
public:
|
||||
matjson::Value json;
|
||||
std::shared_ptr<SettingValue> legacyValue = nullptr;
|
||||
};
|
||||
|
||||
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) {
|
||||
m_impl->json = json;
|
||||
return Ok();
|
||||
Result<std::shared_ptr<LegacyCustomSettingV3>> LegacyCustomSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
|
||||
auto ret = std::make_shared<LegacyCustomSettingV3>(PrivateMarker());
|
||||
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;
|
||||
}
|
||||
bool UnresolvedCustomSettingV3::save(matjson::Value& json) const {
|
||||
bool LegacyCustomSettingV3::save(matjson::Value& json) const {
|
||||
return true;
|
||||
}
|
||||
SettingNodeV3* UnresolvedCustomSettingV3::createNode(float width) {
|
||||
SettingNodeV3* LegacyCustomSettingV3::createNode(float width) {
|
||||
if (m_impl->legacyValue) {
|
||||
return LegacyCustomSettingToV3Node::create(
|
||||
std::static_pointer_cast<UnresolvedCustomSettingV3>(shared_from_this()), width
|
||||
std::static_pointer_cast<LegacyCustomSettingV3>(shared_from_this()), width
|
||||
);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
.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;
|
||||
}
|
||||
|
||||
|
@ -179,6 +158,18 @@ public:
|
|||
|
||||
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 {
|
||||
return m_impl->value;
|
||||
}
|
||||
|
@ -186,19 +177,9 @@ bool BoolSettingV3::getDefaultValue() const {
|
|||
return m_impl->defaultValue;
|
||||
}
|
||||
Result<> BoolSettingV3::isValid(bool value) const {
|
||||
GEODE_UNWRAP(this->isValidShared());
|
||||
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) {
|
||||
if (json.is_bool()) {
|
||||
m_impl->value = json.as_bool();
|
||||
|
@ -244,6 +225,35 @@ public:
|
|||
} 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>()) {}
|
||||
|
||||
int64_t& IntSettingV3::getValueMut() const {
|
||||
|
@ -253,7 +263,6 @@ int64_t IntSettingV3::getDefaultValue() const {
|
|||
return m_impl->defaultValue;
|
||||
}
|
||||
Result<> IntSettingV3::isValid(int64_t value) const {
|
||||
GEODE_UNWRAP(this->isValidShared());
|
||||
if (m_impl->minValue && value < *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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (json.is_number()) {
|
||||
m_impl->value = json.as_int();
|
||||
|
@ -376,6 +359,35 @@ public:
|
|||
|
||||
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 {
|
||||
return m_impl->value;
|
||||
}
|
||||
|
@ -383,7 +395,6 @@ double FloatSettingV3::getDefaultValue() const {
|
|||
return m_impl->defaultValue;
|
||||
}
|
||||
Result<> FloatSettingV3::isValid(double value) const {
|
||||
GEODE_UNWRAP(this->isValidShared());
|
||||
if (m_impl->minValue && value < *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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (json.is_number()) {
|
||||
m_impl->value = json.as_double();
|
||||
|
@ -498,6 +483,22 @@ public:
|
|||
|
||||
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 {
|
||||
return m_impl->value;
|
||||
}
|
||||
|
@ -505,7 +506,6 @@ std::string StringSettingV3::getDefaultValue() const {
|
|||
return m_impl->defaultValue;
|
||||
}
|
||||
Result<> StringSettingV3::isValid(std::string_view value) const {
|
||||
GEODE_UNWRAP(this->isValidShared());
|
||||
if (m_impl->match) {
|
||||
if (!std::regex_match(std::string(value), std::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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (json.is_string()) {
|
||||
m_impl->value = json.as_string();
|
||||
|
@ -582,30 +569,18 @@ public:
|
|||
|
||||
FileSettingV3::FileSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
|
||||
|
||||
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 {
|
||||
GEODE_UNWRAP(this->isValidShared());
|
||||
return Ok();
|
||||
}
|
||||
Result<std::shared_ptr<FileSettingV3>> FileSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
|
||||
auto ret = std::make_shared<FileSettingV3>(PrivateMarker());
|
||||
|
||||
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");
|
||||
|
||||
GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
|
||||
ret->parseSharedProperties(key, modID, root);
|
||||
ret->parseDefaultValue(root, ret->m_impl->defaultValue);
|
||||
ret->m_impl->value = ret->m_impl->defaultValue;
|
||||
|
||||
// Replace known paths like `{gd-save-dir}/`
|
||||
try {
|
||||
m_impl->defaultValue = fmt::format(
|
||||
fmt::runtime(m_impl->defaultValue.string()),
|
||||
ret->m_impl->defaultValue = fmt::format(
|
||||
fmt::runtime(ret->m_impl->defaultValue.string()),
|
||||
fmt::arg("gd-save-dir", dirs::getSaveDir()),
|
||||
fmt::arg("gd-game-dir", dirs::getGameDir()),
|
||||
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&) {
|
||||
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")) {
|
||||
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);
|
||||
}
|
||||
if (!filters.empty()) {
|
||||
m_impl->filters.emplace(filters);
|
||||
ret->m_impl->filters.emplace(filters);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (json.is_string()) {
|
||||
m_impl->value = json.as_string();
|
||||
|
@ -671,6 +661,18 @@ public:
|
|||
|
||||
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 {
|
||||
return m_impl->value;
|
||||
}
|
||||
|
@ -678,19 +680,9 @@ ccColor3B Color3BSettingV3::getDefaultValue() const {
|
|||
return m_impl->defaultValue;
|
||||
}
|
||||
Result<> Color3BSettingV3::isValid(ccColor3B value) const {
|
||||
GEODE_UNWRAP(this->isValidShared());
|
||||
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) {
|
||||
if (json.template is<ccColor3B>()) {
|
||||
m_impl->value = json.template as<ccColor3B>();
|
||||
|
@ -727,6 +719,18 @@ public:
|
|||
|
||||
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 {
|
||||
return m_impl->value;
|
||||
}
|
||||
|
@ -734,19 +738,9 @@ ccColor4B Color4BSettingV3::getDefaultValue() const {
|
|||
return m_impl->defaultValue;
|
||||
}
|
||||
Result<> Color4BSettingV3::isValid(ccColor4B value) const {
|
||||
GEODE_UNWRAP(this->isValidShared());
|
||||
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) {
|
||||
if (json.template is<ccColor4B>()) {
|
||||
m_impl->value = json.template as<ccColor4B>();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -28,7 +28,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
hasBG = !hasBG;
|
||||
|
||||
auto bg = CCLayerColor::create({ 0, 0, 0, 50 });
|
||||
bg->setOpacity(hasBG ? 50 : 0);
|
||||
bg->setOpacity(hasBG ? 60 : 20);
|
||||
|
||||
SettingNodeV3* node;
|
||||
if (auto sett = mod->getSettingV3(key)) {
|
||||
|
@ -40,11 +40,12 @@ bool ModSettingsPopup::setup(Mod* mod) {
|
|||
}
|
||||
|
||||
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);
|
||||
separator->setOpacity(hasBG ? 100 : 50);
|
||||
bg->addChildAtPosition(separator, Anchor::Bottom);
|
||||
// auto separator = CCLayerColor::create({ 0, 0, 0, 50 }, layerSize.width, 1.f);
|
||||
// separator->setOpacity(hasBG ? 100 : 50);
|
||||
// separator->ignoreAnchorPointForPosition(false);
|
||||
// bg->addChildAtPosition(separator, Anchor::Bottom, ccp(0, 0), ccp(.5f, .5f));
|
||||
|
||||
m_settings.push_back(node);
|
||||
|
||||
|
@ -132,11 +133,13 @@ void ModSettingsPopup::onResetAll(CCObject*) {
|
|||
|
||||
void ModSettingsPopup::updateState() {
|
||||
if (this->hasUncommitted()) {
|
||||
m_applyBtnSpr->setColor({0xff, 0xff, 0xff});
|
||||
m_applyBtnSpr->setColor(ccWHITE);
|
||||
m_applyBtnSpr->setOpacity(255);
|
||||
m_applyBtn->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
m_applyBtnSpr->setColor({0x44, 0x44, 0x44});
|
||||
m_applyBtnSpr->setColor(ccGRAY);
|
||||
m_applyBtnSpr->setOpacity(155);
|
||||
m_applyBtn->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +175,7 @@ void ModSettingsPopup::onOpenSaveDirectory(CCObject*) {
|
|||
|
||||
ModSettingsPopup* ModSettingsPopup::create(Mod* mod) {
|
||||
auto ret = new ModSettingsPopup();
|
||||
if (ret->init(440.f, 280.f, mod)) {
|
||||
if (ret->init(440, 280, mod)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue