removing deprecated things, part 1 (does not compile)

This commit is contained in:
altalk23 2024-11-04 20:14:23 +03:00
parent 4dcac338f9
commit 50ab4ebed7
45 changed files with 580 additions and 3306 deletions

View file

@ -5,7 +5,7 @@
#include "loader/Log.hpp"
#include "loader/Mod.hpp"
#include "loader/ModEvent.hpp"
#include "loader/Setting.hpp"
#include "loader/SettingV3.hpp"
#include "loader/Dirs.hpp"
#include <Geode/DefaultInclude.hpp>

View file

@ -6,7 +6,8 @@
#include "ui/EnterLayerEvent.hpp"
#include "ui/BasedButtonSprite.hpp"
#include "ui/IconButtonSprite.hpp"
#include "ui/InputNode.hpp"
#include "ui/Layout.hpp"
#include "ui/SpacerNode.hpp"
#include "ui/General.hpp"
#include "ui/ListView.hpp"
#include "ui/MDPopup.hpp"

View file

@ -37,7 +37,6 @@
#include "../kazmath/include/kazmath/kazmath.h"
#include "../script_support/CCScriptSupport.h"
#include "../include/CCProtocols.h"
#include "Layout.hpp"
#include "../../loader/Event.hpp"
#include <Geode/utils/casts.hpp>
@ -45,6 +44,12 @@
#include <matjson.hpp>
#endif
namespace geode {
class Layout;
class LayoutOptions;
enum class Anchor;
}
NS_CC_BEGIN
class CCCamera;
@ -871,25 +876,13 @@ public:
private:
friend class geode::modifier::FieldContainer;
[[deprecated("Will be removed, it's an ABI break")]]
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer();
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(char const* forClass);
GEODE_DLL void addEventListenerInternal(
std::string const& id,
geode::EventListenerProtocol* protocol
);
#ifdef GEODE_EXPORTING
[[deprecated("Will be removed, it's an ABI break")]]
GEODE_DLL std::optional<matjson::Value> getAttributeInternal(std::string const& attribute);
#endif
public:
#ifdef GEODE_EXPORTING
[[deprecated("Will be removed, it's an ABI break")]]
GEODE_DLL void setAttribute(std::string const& attribute, matjson::Value const& value);
#endif
public:
/**
* Get the string ID of this node
* @returns The ID, or an empty string if the node has no ID.
@ -990,13 +983,13 @@ public:
* CCLayers / CCMenus, this will change where the children are located
* @note Geode addition
*/
GEODE_DLL void setLayout(Layout* layout, bool apply = true, bool respectAnchor = true);
GEODE_DLL void setLayout(geode::Layout* layout, bool apply = true, bool respectAnchor = true);
/**
* Get the Layout for this node
* @returns The current layout, or nullptr if no layout is set
* @note Geode addition
*/
GEODE_DLL Layout* getLayout();
GEODE_DLL geode::Layout* getLayout();
/**
* Update the layout of this node using the current Layout. If no layout is
* set, nothing happens
@ -1011,13 +1004,13 @@ public:
* @param apply Whether to update the layout of the parent node
* @note Geode addition
*/
GEODE_DLL void setLayoutOptions(LayoutOptions* options, bool apply = true);
GEODE_DLL void setLayoutOptions(geode::LayoutOptions* options, bool apply = true);
/**
* Get the layout options for this node
* @returns The current layout options, or nullptr if no options are set
* @note Geode addition
*/
GEODE_DLL LayoutOptions* getLayoutOptions();
GEODE_DLL geode::LayoutOptions* getLayoutOptions();
/**
* Adds a child at an anchored position with an offset. The node is placed
* in its parent where the anchor specifies, and then the offset is used to
@ -1029,7 +1022,7 @@ public:
* if no other layout is already specified
* @note Geode addition
*/
GEODE_DLL void addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
GEODE_DLL void addChildAtPosition(CCNode* child, geode::Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
/**
* Adds a child at an anchored position with an offset. The node is placed
* in its parent where the anchor specifies, and then the offset is used to
@ -1044,7 +1037,7 @@ public:
*/
GEODE_DLL void addChildAtPosition(
CCNode* child,
Anchor anchor,
geode::Anchor anchor,
CCPoint const& offset,
CCPoint const& nodeAnchor,
bool useAnchorLayout = true
@ -1057,7 +1050,7 @@ public:
* @param offset Where to place the child relative to the anchor
* @note Geode addition
*/
GEODE_DLL void updateAnchoredPosition(Anchor anchor, CCPoint const& offset = CCPointZero);
GEODE_DLL void updateAnchoredPosition(geode::Anchor anchor, CCPoint const& offset = CCPointZero);
/**
* Updates the anchored position of a child. Requires the child to already
* have a parent; if the child already has AnchorLayoutOptions set, those
@ -1068,7 +1061,7 @@ public:
* @note Geode addition
*/
GEODE_DLL void updateAnchoredPosition(
Anchor anchor,
geode::Anchor anchor,
CCPoint const& offset,
CCPoint const& nodeAnchor
);

View file

@ -59,7 +59,6 @@ THE SOFTWARE.
// base_nodes
#include "../base_nodes/CCNode.h"
#include "../base_nodes/CCAtlasNode.h"
#include "../base_nodes/SpacerNode.hpp"
// cocoa
#include "../cocoa/CCAffineTransform.h"

View file

@ -9,7 +9,6 @@
#include "Loader.hpp" // very nice circular dependency fix
#include "Hook.hpp"
#include "ModMetadata.hpp"
#include "Setting.hpp"
#include "SettingV3.hpp"
#include "Types.hpp"
#include "Loader.hpp"
@ -108,8 +107,6 @@ namespace geode {
std::string getID() const;
std::string getName() const;
[[deprecated("Use Mod::getDevelopers instead")]]
std::string getDeveloper() const;
std::vector<std::string> getDevelopers() const;
std::optional<std::string> getDescription() const;
std::optional<std::string> getDetails() const;
@ -137,17 +134,6 @@ namespace geode {
std::vector<Mod*> getDependants() const;
#endif
/**
* Check if this Mod has updates available on the mods index. If
* you're using this for automatic update checking, use
* `openInfoPopup` or `openIndexPopup` from the `ui/GeodeUI.hpp`
* header to open the Mod's page to let the user install the update
* @returns The latest available version on the index if there are
* updates for this mod
*/
[[deprecated("Use Mod::checkUpdates instead; this function always returns nullopt")]]
std::optional<VersionInfo> hasAvailableUpdate() const;
using CheckUpdatesTask = Task<Result<std::optional<VersionInfo>, std::string>>;
/**
* Check if this Mod has updates available on the mods index. If
@ -189,12 +175,6 @@ namespace geode {
std::vector<std::string> getSettingKeys() const;
bool hasSetting(std::string_view const key) const;
// todo in v4: remove these
[[deprecated("Use Mod::getSettingV3")]]
std::optional<Setting> getSettingDefinition(std::string_view const key) const;
[[deprecated("Use Mod::getSettingV3")]]
SettingValue* getSetting(std::string_view const key) const;
// todo in v4: possibly rename this to getSetting?
/**
* Get the definition of a setting, or null if the setting was not found,
@ -204,36 +184,6 @@ namespace geode {
*/
std::shared_ptr<SettingV3> getSettingV3(std::string_view const key) const;
/**
* Register a custom setting's value class. See Mod::addCustomSetting
* for a convenience wrapper that creates the value in-place to avoid
* code duplication. Also see
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* more information about custom settings
* @param key The setting's key
* @param value The SettingValue class that shall handle this setting
* @see addCustomSetting
*/
[[deprecated("Use Mod::registerCustomSettingType")]]
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
/**
* Register a custom setting's value class. The new SettingValue class
* will be created in-place using `std::make_unique`. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* more information about custom settings
* @param key The setting's key
* @param value The value of the custom setting
* @example
* $on_mod(Loaded) {
* Mod::get()->addCustomSetting<MySettingValue>("setting-key", DEFAULT_VALUE);
* }
*/
template <class T, class V>
[[deprecated("Use Mod::registerCustomSettingType")]]
void addCustomSetting(std::string_view const key, V const& value) {
this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value));
}
/**
* Register a custom setting type. See
* [the setting docs](https://docs.geode-sdk.org/mods/settings) for more

View file

@ -2,7 +2,6 @@
#include "../utils/Result.hpp"
#include "../utils/VersionInfo.hpp"
#include "Setting.hpp"
#include "Types.hpp"
#include <matjson.hpp>
@ -123,13 +122,6 @@ namespace geode {
* character set.
*/
[[nodiscard]] std::string getName() const;
/**
* The name of the head developer.
* If the mod has multiple * developers, this will return the first
* developer in the list.
*/
[[nodiscard, deprecated("Use ModMetadata::getDevelopers() instead")]]
std::string getDeveloper() const;
/**
* The developers of this mod
*/
@ -155,11 +147,6 @@ namespace geode {
* (see MDTextArea for more info)
*/
[[nodiscard]] std::optional<std::string> getSupportInfo() const;
/**
* Git Repository of the mod
*/
[[nodiscard, deprecated("Use ModMetadata::getLinks instead")]]
std::optional<std::string> getRepository() const;
/**
* Get the links (related websites / servers / etc.) for this mod
*/
@ -180,11 +167,6 @@ namespace geode {
* Mod spritesheet names
*/
[[nodiscard]] std::vector<std::string> getSpritesheets() const;
/**
* Mod settings
* @note Not a map because insertion order must be preserved
*/
[[nodiscard, deprecated("Use getSettingsV3")]] std::vector<std::pair<std::string, Setting>> getSettings() const;
/**
* Mod settings
* @note Not a map because insertion order must be preserved
@ -237,8 +219,6 @@ namespace geode {
void setDependencies(std::vector<Dependency> const& value);
void setIncompatibilities(std::vector<Incompatibility> const& value);
void setSpritesheets(std::vector<std::string> const& value);
[[deprecated("This function does NOTHING")]]
void setSettings(std::vector<std::pair<std::string, Setting>> const& value);
void setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value);
void setTags(std::unordered_set<std::string> const& value);
void setNeedsEarlyLoad(bool const& value);

View file

@ -1,324 +0,0 @@
#pragma once
#include "Types.hpp"
#include "../DefaultInclude.hpp"
#include "../utils/Result.hpp"
#include "../utils/file.hpp"
#include <matjson.hpp>
#include <optional>
#include <unordered_set>
#include <cocos2d.h>
#pragma warning(push)
#pragma warning(disable : 4275)
namespace geode {
class SettingNode;
class SettingValue;
struct JsonMaybeObject;
struct JsonMaybeValue;
/**
* A Setting for a boolean value. Represented in-game as a simple toggle
*/
struct GEODE_DLL BoolSetting final {
using ValueType = bool;
std::optional<std::string> name;
std::optional<std::string> description;
bool defaultValue;
static Result<BoolSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for an integer value. The value can be limited using the min
* and max options
*/
struct GEODE_DLL IntSetting final {
using ValueType = int64_t;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
std::optional<ValueType> min;
std::optional<ValueType> max;
struct {
bool arrows = true;
bool bigArrows = false;
size_t arrowStep = 1;
size_t bigArrowStep = 5;
bool slider = true;
std::optional<ValueType> sliderStep = std::nullopt;
bool input = true;
} controls;
static Result<IntSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for a float value. The value can be limited using the min
* and max options
*/
struct GEODE_DLL FloatSetting final {
using ValueType = double;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
std::optional<ValueType> min;
std::optional<ValueType> max;
struct {
bool arrows = true;
bool bigArrows = false;
size_t arrowStep = 1;
size_t bigArrowStep = 5;
bool slider = true;
std::optional<ValueType> sliderStep = std::nullopt;
bool input = true;
} controls;
static Result<FloatSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for a string value
*/
struct GEODE_DLL StringSetting final {
using ValueType = std::string;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
struct Data {
/**
* A regex the string must successfully match against
*/
std::optional<std::string> match;
/**
* The CCTextInputNode's allowed character filter
*/
std::optional<std::string> filter;
/**
* A list of options the user can choose from
*/
std::optional<std::vector<std::string>> options;
};
std::unique_ptr<Data> controls;
std::array<uint8_t, sizeof(Data::match) + sizeof(Data::filter) - sizeof(controls)> m_padding;
StringSetting();
StringSetting(StringSetting const& other);
StringSetting(StringSetting&& other) noexcept;
StringSetting& operator=(StringSetting const& other);
StringSetting& operator=(StringSetting&& other) noexcept;
~StringSetting();
static Result<StringSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for a file input. Lets the user select a file from their
* local device
*/
struct GEODE_DLL FileSetting final {
using ValueType = std::filesystem::path;
using Filter = utils::file::FilePickOptions::Filter;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
struct {
std::vector<Filter> filters;
} controls;
static Result<FileSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for an RGB color. See ColorAlphaSetting for a setting that
* also allows customizing alpha
*/
struct GEODE_DLL ColorSetting final {
using ValueType = cocos2d::ccColor3B;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
static Result<ColorSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for an RGBA color. See ColorSetting for a setting that doesn't
* have alpha
*/
struct GEODE_DLL ColorAlphaSetting final {
using ValueType = cocos2d::ccColor4B;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
static Result<ColorAlphaSetting> parse(JsonMaybeObject& obj);
};
/**
* A custom setting, defined by the mod. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
* information about how to create custom settings
*/
struct GEODE_DLL CustomSetting final {
std::shared_ptr<ModJson> json;
};
using SettingKind = std::variant<
BoolSetting,
IntSetting,
FloatSetting,
StringSetting,
FileSetting,
ColorSetting,
ColorAlphaSetting,
CustomSetting
>;
/**
* Represents a saved value for a mod that can be customized by the user
* through an in-game UI. This class is for modeling the setting's
* definition - what values are accepted, its name etc.
* See [the tutorial page](https://docs.geode-sdk.org/mods/settings)
* for more information about how settings work
* @see SettingValue
* @see SettingNode
*/
struct GEODE_DLL Setting final {
private:
std::string m_key;
std::string m_modID;
SettingKind m_kind;
Setting() = default;
public:
static Result<Setting> parse(
std::string const& key,
std::string const& mod,
JsonMaybeValue& obj
);
Setting(
std::string const& key,
std::string const& mod,
SettingKind const& kind
);
template<class T>
std::optional<T> get() {
if (std::holds_alternative<T>(m_kind)) {
return std::get<T>(m_kind);
}
return std::nullopt;
}
std::unique_ptr<SettingValue> createDefaultValue() const;
bool isCustom() const;
std::string getDisplayName() const;
std::optional<std::string> getDescription() const;
std::string getModID() const;
};
/**
* Stores the actual, current value of a Setting. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
* information, and how to create custom settings
*/
class GEODE_DLL SettingValue {
protected:
std::string m_key;
std::string m_modID;
SettingValue(std::string const& key, std::string const& mod);
void valueChanged();
public:
virtual ~SettingValue() = default;
virtual bool load(matjson::Value const& json) = 0;
virtual bool save(matjson::Value& json) const = 0;
virtual SettingNode* createNode(float width) = 0;
std::string getKey() const;
std::string getModID() const;
};
template<class T>
class GeodeSettingValue final : public SettingValue {
public:
using ValueType = typename T::ValueType;
protected:
ValueType m_value;
T m_definition;
using Valid = std::pair<ValueType, std::optional<std::string>>;
GEODE_DLL Valid toValid(ValueType const& value) const;
public:
GeodeSettingValue(std::string const& key, std::string const& modID, T const& definition)
: SettingValue(key, modID),
m_definition(definition),
m_value(definition.defaultValue) {}
bool load(matjson::Value const& json) override;
bool save(matjson::Value& json) const;
GEODE_DLL SettingNode* createNode(float width) override;
T castDefinition() const {
return m_definition;
}
Setting getDefinition() const {
return Setting(m_key, m_modID, m_definition);
}
GEODE_DLL ValueType getValue() const;
GEODE_DLL void setValue(ValueType const& value);
GEODE_DLL Result<> validate(ValueType const& value) const;
};
using BoolSettingValue = GeodeSettingValue<BoolSetting>;
using IntSettingValue = GeodeSettingValue<IntSetting>;
using FloatSettingValue = GeodeSettingValue<FloatSetting>;
using StringSettingValue = GeodeSettingValue<StringSetting>;
using FileSettingValue = GeodeSettingValue<FileSetting>;
using ColorSettingValue = GeodeSettingValue<ColorSetting>;
using ColorAlphaSettingValue = GeodeSettingValue<ColorAlphaSetting>;
// todo: remove in v3
template<class T>
struct [[deprecated("Use SettingTypeForValueType from SettingV3 instead")]] GEODE_DLL SettingValueSetter {
static T get(SettingValue* setting);
static void set(SettingValue* setting, T const& value);
};
template<class T>
bool GeodeSettingValue<T>::load(matjson::Value const& json) {
if (!json.is<ValueType>()) return false;
m_value = json.as<ValueType>();
return true;
}
template<class T>
bool GeodeSettingValue<T>::save(matjson::Value& json) const {
json = m_value;
return true;
}
}
#pragma warning(pop)

View file

@ -1,63 +0,0 @@
#pragma once
#include "Event.hpp"
#include "Loader.hpp"
#include "Setting.hpp"
#include "Mod.hpp"
#include "SettingV3.hpp"
#include <optional>
namespace geode {
struct GEODE_DLL [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] SettingChangedEvent : public Event {
Mod* mod;
SettingValue* value;
SettingChangedEvent(Mod* mod, SettingValue* value);
};
class GEODE_DLL [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] SettingChangedFilter : public EventFilter<SettingChangedEvent> {
protected:
std::string m_modID;
std::optional<std::string> m_targetKey;
public:
using Callback = void(SettingValue*);
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEvent* event);
/**
* Listen to changes on a setting, or all settings
* @param modID Mod whose settings to listen to
* @param settingID Setting to listen to, or all settings if nullopt
*/
SettingChangedFilter(
std::string const& modID,
std::optional<std::string> const& settingKey
);
SettingChangedFilter(SettingChangedFilter const&) = default;
};
/**
* Listen for built-in setting changes
*/
template<class T>
class [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] GeodeSettingChangedFilter : public SettingChangedFilter {
public:
using Callback = void(T);
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEvent* event) {
if (
m_modID == event->mod->getID() &&
(!m_targetKey || m_targetKey.value() == event->value->getKey())
) {
fn(SettingValueSetter<T>::get(event->value));
}
return ListenerResult::Propagate;
}
GeodeSettingChangedFilter(
std::string const& modID,
std::string const& settingID
) : SettingChangedFilter(modID, settingID) {}
GeodeSettingChangedFilter(GeodeSettingChangedFilter const&) = default;
};
}

View file

@ -1,32 +0,0 @@
#pragma once
#include "Setting.hpp"
#include <cocos2d.h>
namespace geode {
class SettingNode;
struct SettingNodeDelegate {
virtual void settingValueChanged(SettingNode* node) {}
virtual void settingValueCommitted(SettingNode* node) {}
};
class GEODE_DLL SettingNode : public cocos2d::CCNode {
protected:
SettingValue* m_value;
SettingNodeDelegate* m_delegate = nullptr;
bool init(SettingValue* value);
void dispatchChanged();
void dispatchCommitted();
public:
void setDelegate(SettingNodeDelegate* delegate);
virtual void commit() = 0;
virtual bool hasUncommittedChanges() = 0;
virtual bool hasNonDefaultValue() = 0;
virtual void resetToDefault() = 0;
};
}

View file

@ -3,15 +3,12 @@
#include "../DefaultInclude.hpp"
#include <optional>
#include <cocos2d.h>
// todo: remove this header in 4.0.0
#include "Setting.hpp"
#include "../utils/cocos.hpp"
#include "../utils/file.hpp"
// this unfortunately has to be included because of C++ templates
#include "../utils/JsonValidation.hpp"
#include "../utils/function.hpp"
// todo in v4: this can be removed as well as the friend decl in LegacyCustomSettingV3
class LegacyCustomSettingToV3Node;
class ModSettingsPopup;
namespace geode {
@ -183,17 +180,6 @@ namespace geode {
* Reset this setting's value back to its original value
*/
virtual void reset() = 0;
[[deprecated(
"This function will be removed alongside legacy settings in 4.0.0! "
"You should NOT be implementing it for your own custom setting classes"
)]]
virtual std::optional<Setting> convertToLegacy() const;
[[deprecated(
"This function will be removed alongside legacy settings in 4.0.0! "
"You should NOT be implementing it for your own custom setting classes"
)]]
virtual std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const;
};
using SettingGenerator = std::function<Result<std::shared_ptr<SettingV3>>(
@ -357,37 +343,6 @@ namespace geode {
void reset() override;
};
// 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 ::geode::ModSettingsManager;
friend class ::LegacyCustomSettingToV3Node;
private:
class PrivateMarker {};
friend class SettingV3;
public:
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;
SettingNodeV3* createNode(float width) override;
bool isDefaultValue() const override;
void reset() override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL BoolSettingV3 final : public SettingBaseValueV3<bool> {
private:
class Impl;
@ -404,9 +359,6 @@ namespace geode {
Result<> isValid(bool value) const override;
SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL IntSettingV3 final : public SettingBaseValueV3<int64_t> {
@ -436,9 +388,6 @@ namespace geode {
bool isInputEnabled() const;
SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL FloatSettingV3 final : public SettingBaseValueV3<double> {
@ -468,9 +417,6 @@ namespace geode {
bool isInputEnabled() const;
SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL StringSettingV3 final : public SettingBaseValueV3<std::string, std::string_view> {
@ -493,9 +439,6 @@ namespace geode {
std::optional<std::vector<std::string>> getEnumOptions() const;
SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL FileSettingV3 final : public SettingBaseValueV3<std::filesystem::path, std::filesystem::path const&> {
@ -519,9 +462,6 @@ namespace geode {
std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const;
SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL Color3BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor3B> {
@ -540,9 +480,6 @@ namespace geode {
Result<> isValid(cocos2d::ccColor3B value) const override;
SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL Color4BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor4B> {
@ -561,9 +498,6 @@ namespace geode {
Result<> isValid(cocos2d::ccColor4B value) const override;
SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL SettingNodeV3 : public cocos2d::CCNode {

View file

@ -133,11 +133,6 @@ namespace geode {
*/
GEODE_DLL void openSupportPopup(Mod* mod);
GEODE_DLL void openSupportPopup(ModMetadata const& metadata);
/**
* Open the store page for a mod (if it exists)
*/
[[deprecated("Will be removed, use openInfoPopup instead")]]
GEODE_DLL void openIndexPopup(Mod* mod);
/**
* Open the settings popup for a mod (if it has any settings)
*/

View file

@ -1,41 +0,0 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include <Geode/binding/CCTextInputNode.hpp>
#include <cocos2d.h>
namespace geode {
class GEODE_DLL InputNode : public cocos2d::CCMenuItem {
protected:
cocos2d::extension::CCScale9Sprite* m_bgSprite;
CCTextInputNode* m_input;
bool init(float, float, char const*, char const*, std::string const&, int);
bool init(float, char const*, char const*, std::string const&, int);
public:
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(
float width, char const* placeholder, char const* fontFile, std::string const& filter,
int limit
);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(
float width, char const* placeholder, std::string const& filter, int limit
);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(float width, char const* placeholder, std::string const& filter);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(float width, char const* placeholder, char const* fontFile);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(float width, char const* placeholder);
CCTextInputNode* getInput() const;
cocos2d::extension::CCScale9Sprite* getBG() const;
void setEnabled(bool enabled) override;
void setString(std::string const&);
std::string getString();
};
}

View file

@ -1,448 +1,430 @@
#pragma once
#include "../include/ccMacros.h"
#include "../cocoa/CCAffineTransform.h"
#include "../cocoa/CCArray.h"
#include <Geode/platform/platform.hpp>
#include <optional>
#include <memory>
NS_CC_BEGIN
class CCNode;
#pragma warning(push)
#pragma warning(disable: 4275)
/**
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
* to apply a layout to a node, and then use CCNode::updateLayout to apply
* the layout's positioning. Geode comes with a few default layouts like
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
* of layout you can inherit from the Layout class.
*/
class GEODE_DLL Layout : public CCObject {
protected:
CCArray* getNodesToPosition(CCNode* forNode) const;
bool m_ignoreInvisibleChildren = false;
public:
/**
* Automatically apply the layout's positioning on a set of nodes
* @param on Node to apply the layout on. Position's the node's children
* according to the layout. The content size of the node should be
* respected as a boundary the layout shouldn't overflow. The node may be
* rescaled to better fit its contents
*/
virtual void apply(CCNode* on) = 0;
/**
* Get how much space this layout would like to take up for a given target
*/
virtual CCSize getSizeHint(CCNode* on) const = 0;
void ignoreInvisibleChildren(bool ignore);
bool isIgnoreInvisibleChildren() const;
virtual ~Layout() = default;
};
class GEODE_DLL LayoutOptions : public CCObject {
public:
virtual ~LayoutOptions() = default;
};
/**
* The direction of an AxisLayout
*/
enum class Axis {
Row,
Column,
};
/**
* Specifies the alignment of something in an AxisLayout
*/
enum class AxisAlignment {
// Align items to the start
// |ooo......|
Start,
// All items are centered
// |...ooo...|
Center,
// Align items to the end
// |......ooo|
End,
// Each item gets the same portion from the layout (disregards gap)
// |.o..o..o.|
Even,
// Space between each item is the same (disregards gap)
// |o...o...o|
Between,
};
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
/**
* Options for controlling the behaviour of individual nodes in an AxisLayout
* @example
* auto node = CCNode::create();
* // this node will have 10 units of spacing between it and the next one
* node->setLayoutOptions(
* AxisLayoutOptions::create()
* ->setNextGap(10.f)
* );
* someNodeWithALayout->addChild(node);
*/
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
protected:
class Impl;
std::unique_ptr<Impl> m_impl;
AxisLayoutOptions();
public:
static AxisLayoutOptions* create();
virtual ~AxisLayoutOptions();
std::optional<bool> getAutoScale() const;
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
float getMaxScale() const;
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
float getMinScale() const;
bool hasExplicitMaxScale() const;
bool hasExplicitMinScale() const;
float getRelativeScale() const;
std::optional<float> getLength() const;
std::optional<float> getPrevGap() const;
std::optional<float> getNextGap() const;
bool getBreakLine() const;
bool getSameLine() const;
int getScalePriority() const;
std::optional<AxisAlignment> getCrossAxisAlignment() const;
/**
* Set the maximum scale this node can be if it's contained in an
* auto-scaled layout. Default is 1
*/
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
AxisLayoutOptions* setMaxScale(float scale);
/**
* Set the minimum scale this node can be if it's contained in an
* auto-scaled layout. Default is AXISLAYOUT_DEFAULT_MIN_SCALE
*/
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
AxisLayoutOptions* setMinScale(float scale);
/**
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
* uses the parent layout's default min / max scales
*/
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
/**
* Set the relative scale of this node compared to other nodes if it's
* contained in an auto-scaled layout. Default is 1
*/
AxisLayoutOptions* setRelativeScale(float scale);
/**
* Set auto-scaling for this node, overriding the layout's auto-scale
* setting. If nullopt, the layout's auto-scale options will be used
*/
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
/**
* Set an absolute length for this node. If nullopt, the length will be
* dynamically calculated based on content size
*/
AxisLayoutOptions* setLength(std::optional<float> length);
/**
* Override the default gap in the layout between this node and the
* previous one. If nullopt, the default gap of the layout will be used
*/
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
/**
* Override the default gap in the layout between this node and the next
* one. If nullopt, the default gap of the layout will be used
*/
AxisLayoutOptions* setNextGap(std::optional<float> gap);
/**
* If enabled, the node will always cause a growable axis layout to break
* into a new line even if the current line could've fit the next node
*/
AxisLayoutOptions* setBreakLine(bool enable);
/**
* If enabled, the node will be forced to be on the same line as the
* previous node even if doing this would overflow
*/
AxisLayoutOptions* setSameLine(bool enable);
/**
* Set the scale priority of this node. Nodes with higher priority will be
* scaled down first before nodes with lower priority when an auto-scaled
* layout attempts to fit its contents. Default is
* AXISLAYOUT_DEFAULT_PRIORITY
* @note For optimal performance, the priorities should all be close to
* each other with no gaps
*/
AxisLayoutOptions* setScalePriority(int priority);
/**
* Override the cross axis alignment for this node in the layout
*/
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
};
/**
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be
* used to arrange nodes in a single line, a grid, or a flex layout. The
* RowLayout and ColumnLayout classes function as simple thin wrappers over
* AxisLayout. The positioning of individual nodes in the layout can be
* further controlled using AxisLayoutOptions
* @warning Calculating layouts can get increasingly expensive for large
* amounts of child nodes being fit into a small space - while this should
* never prove a real performance concern as most layouts only have a few
* hundred children at the very most, be aware that you probably shouldn't
* call CCNode::updateLayout every frame for a menu with thousands of children
* @example
* auto menu = CCMenu::create();
* // The menu's children will be arranged horizontally, unless they overflow
* // the content size width in which case a new line will be inserted and
* // aligned to the left. The menu automatically will automatically grow in
* // height to fit all the rows
* menu->setLayout(
* RowLayout::create()
* ->setGap(10.f)
* ->setGrowCrossAxis(true)
* ->setAxisAlignment(AxisAlignment::Start)
* );
* menu->setContentSize({ 200.f, 0.f });
* menu->addChild(...);
* menu->updateLayout();
*/
class GEODE_DLL AxisLayout : public Layout {
protected:
class Impl;
std::unique_ptr<Impl> m_impl;
AxisLayout(Axis);
public:
/**
* Create a new AxisLayout. Note that this class is not automatically
* managed by default, so you must assign it to a CCNode or manually
* manage the memory yourself. See the chainable setters on AxisLayout for
* what options you can customize for the layout
* @param axis The direction of the layout
* @note For convenience, you can use the RowLayout and ColumnLayout
* classes, which are just thin wrappers over AxisLayout
* @returns Created AxisLayout
*/
static AxisLayout* create(Axis axis = Axis::Row);
virtual ~AxisLayout();
void apply(CCNode* on) override;
CCSize getSizeHint(CCNode* on) const override;
Axis getAxis() const;
AxisAlignment getAxisAlignment() const;
AxisAlignment getCrossAxisAlignment() const;
AxisAlignment getCrossAxisLineAlignment() const;
float getGap() const;
bool getAxisReverse() const;
bool getCrossAxisReverse() const;
bool getAutoScale() const;
bool getGrowCrossAxis() const;
bool getCrossAxisOverflow() const;
std::optional<float> getAutoGrowAxis() const;
float getDefaultMinScale() const;
float getDefaultMaxScale() const;
AxisLayout* setAxis(Axis axis);
/**
* Sets where to align the target node's children on the main axis (X-axis
* for Row, Y-axis for Column)
*/
AxisLayout* setAxisAlignment(AxisAlignment align);
/**
* Sets where to align the target node's children on the cross-axis (Y-axis
* for Row, X-axis for Column)
*/
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
/**
* Sets where to align the target node's children on the cross-axis for
* each row (Y-axis for Row, X-axis for Column)
*/
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
/**
* The spacing between the children of the node this layout applies to.
* Measured as the space between their edges, not centres. Does not apply
* on the main / cross axis if their alignment is AxisAlignment::Even
*/
AxisLayout* setGap(float gap);
/**
* Whether to reverse the direction of the children in this layout or not
*/
AxisLayout* setAxisReverse(bool reverse);
/**
* Whether to reverse the direction of the rows on the cross-axis or not
*/
AxisLayout* setCrossAxisReverse(bool reverse);
/**
* If enabled, then the layout may scale the target's children if they are
* about to overflow. Assumes that all the childrens' intended scale is 1
*/
AxisLayout* setAutoScale(bool enable);
/**
* If true, if the main axis overflows extra nodes will be placed on new
* rows/columns on the cross-axis
*/
AxisLayout* setGrowCrossAxis(bool expand);
/**
* If true, the cross-axis content size of the target node will be
* automatically adjusted to fit the children
*/
AxisLayout* setCrossAxisOverflow(bool allow);
/**
* If not `std::nullopt`, then the axis will be automatically extended to
* fit all items in a single row whose minimum length is the specified.
* Useful for scrollable list layer contents
*/
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
/**
* Set the default minimum/maximum scales for nodes in the layout
*/
AxisLayout* setDefaultScaleLimits(float min, float max);
};
/**
* Simple layout for arranging nodes in a row (horizontal line)
*/
class GEODE_DLL RowLayout : public AxisLayout {
protected:
RowLayout();
public:
/**
* Create a new RowLayout. See the chainable setters on RowLayout for
* what options you can customize for the layout
* @returns Created RowLayout
*/
static RowLayout* create();
};
/**
* Simple layout for arranging nodes in a column (vertical line)
*/
class GEODE_DLL ColumnLayout : public AxisLayout {
protected:
ColumnLayout();
public:
/**
* Create a new ColumnLayout. See the chainable setters on RowLayout for
* what options you can customize for the layout
* @returns Created ColumnLayout
*/
static ColumnLayout* create();
};
/**
* The relative position of a node to its parent in an AnchorLayout
*/
enum class Anchor {
Center,
TopLeft,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left,
};
/**
* Options for customizing a node's position in an AnchorLayout
*/
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
protected:
Anchor m_anchor = Anchor::Center;
CCPoint m_offset = CCPointZero;
public:
static AnchorLayoutOptions* create();
Anchor getAnchor() const;
CCPoint getOffset() const;
AnchorLayoutOptions* setAnchor(Anchor anchor);
AnchorLayoutOptions* setOffset(CCPoint const& offset);
};
/**
* A layout for positioning nodes at specific positions relative to their
* parent's content size. See `Anchor` for available anchoring options. Useful
* for example for popups, where a popup using `AnchorLayout` can be
* automatically resized without needing to manually shuffle nodes around
*/
class GEODE_DLL AnchorLayout : public Layout {
public:
static AnchorLayout* create();
void apply(CCNode* on) override;
CCSize getSizeHint(CCNode* on) const override;
/**
* Get a position according to anchoring rules, with the same algorithm as
* `AnchorLayout` uses to position its nodes
* @param in The node whose content size to use as a reference
* @param anchor The anchor position
* @param offset Offset from the anchor
* @returns A position in `in` for the anchored and offsetted location
*/
static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset);
};
/**
* A layout for automatically copying the content size of a node to other nodes.
* Basically main use case is for FLAlertLayers (setting the size of the
* background and `m_buttonMenu` based on `m_mainLayer`)
*/
class GEODE_DLL CopySizeLayout : public cocos2d::AnchorLayout {
protected:
cocos2d::CCArray* m_targets;
public:
static CopySizeLayout* create();
virtual ~CopySizeLayout();
/**
* Add a target to be automatically resized. Any targets' layouts will
* also be updated when this layout is updated
*/
CopySizeLayout* add(cocos2d::CCNode* target);
/**
* Remove a target from being automatically resized
*/
CopySizeLayout* remove(cocos2d::CCNode* target);
void apply(cocos2d::CCNode* in) override;
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
};
#pragma warning(pop)
NS_CC_END
#pragma once
#include <cocos2d.h>
#include <Geode/platform/platform.hpp>
#include <optional>
#include <memory>
namespace geode {
#pragma warning(push)
#pragma warning(disable: 4275)
/**
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
* to apply a layout to a node, and then use CCNode::updateLayout to apply
* the layout's positioning. Geode comes with a few default layouts like
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
* of layout you can inherit from the Layout class.
*/
class GEODE_DLL Layout : public cocos2d::CCObject {
protected:
cocos2d::CCArray* getNodesToPosition(cocos2d::CCNode* forNode) const;
bool m_ignoreInvisibleChildren = false;
public:
/**
* Automatically apply the layout's positioning on a set of nodes
* @param on Node to apply the layout on. Position's the node's children
* according to the layout. The content size of the node should be
* respected as a boundary the layout shouldn't overflow. The node may be
* rescaled to better fit its contents
*/
virtual void apply(cocos2d::CCNode* on) = 0;
/**
* Get how much space this layout would like to take up for a given target
*/
virtual cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const = 0;
void ignoreInvisibleChildren(bool ignore);
bool isIgnoreInvisibleChildren() const;
virtual ~Layout() = default;
};
class GEODE_DLL LayoutOptions : public cocos2d::CCObject {
public:
virtual ~LayoutOptions() = default;
};
/**
* The direction of an AxisLayout
*/
enum class Axis {
Row,
Column,
};
/**
* Specifies the alignment of something in an AxisLayout
*/
enum class AxisAlignment {
// Align items to the start
// |ooo......|
Start,
// All items are centered
// |...ooo...|
Center,
// Align items to the end
// |......ooo|
End,
// Each item gets the same portion from the layout (disregards gap)
// |.o..o..o.|
Even,
// Space between each item is the same (disregards gap)
// |o...o...o|
Between,
};
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
/**
* Options for controlling the behaviour of individual nodes in an AxisLayout
* @example
* auto node = CCNode::create();
* // this node will have 10 units of spacing between it and the next one
* node->setLayoutOptions(
* AxisLayoutOptions::create()
* ->setNextGap(10.f)
* );
* someNodeWithALayout->addChild(node);
*/
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
protected:
class Impl;
std::unique_ptr<Impl> m_impl;
AxisLayoutOptions();
public:
static AxisLayoutOptions* create();
virtual ~AxisLayoutOptions();
std::optional<bool> getAutoScale() const;
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
float getMaxScale() const;
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
float getMinScale() const;
bool hasExplicitMaxScale() const;
bool hasExplicitMinScale() const;
float getRelativeScale() const;
std::optional<float> getLength() const;
std::optional<float> getPrevGap() const;
std::optional<float> getNextGap() const;
bool getBreakLine() const;
bool getSameLine() const;
int getScalePriority() const;
std::optional<AxisAlignment> getCrossAxisAlignment() const;
/**
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
* uses the parent layout's default min / max scales
*/
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
/**
* Set the relative scale of this node compared to other nodes if it's
* contained in an auto-scaled layout. Default is 1
*/
AxisLayoutOptions* setRelativeScale(float scale);
/**
* Set auto-scaling for this node, overriding the layout's auto-scale
* setting. If nullopt, the layout's auto-scale options will be used
*/
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
/**
* Set an absolute length for this node. If nullopt, the length will be
* dynamically calculated based on content size
*/
AxisLayoutOptions* setLength(std::optional<float> length);
/**
* Override the default gap in the layout between this node and the
* previous one. If nullopt, the default gap of the layout will be used
*/
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
/**
* Override the default gap in the layout between this node and the next
* one. If nullopt, the default gap of the layout will be used
*/
AxisLayoutOptions* setNextGap(std::optional<float> gap);
/**
* If enabled, the node will always cause a growable axis layout to break
* into a new line even if the current line could've fit the next node
*/
AxisLayoutOptions* setBreakLine(bool enable);
/**
* If enabled, the node will be forced to be on the same line as the
* previous node even if doing this would overflow
*/
AxisLayoutOptions* setSameLine(bool enable);
/**
* Set the scale priority of this node. Nodes with higher priority will be
* scaled down first before nodes with lower priority when an auto-scaled
* layout attempts to fit its contents. Default is
* AXISLAYOUT_DEFAULT_PRIORITY
* @note For optimal performance, the priorities should all be close to
* each other with no gaps
*/
AxisLayoutOptions* setScalePriority(int priority);
/**
* Override the cross axis alignment for this node in the layout
*/
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
};
/**
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be
* used to arrange nodes in a single line, a grid, or a flex layout. The
* RowLayout and ColumnLayout classes function as simple thin wrappers over
* AxisLayout. The positioning of individual nodes in the layout can be
* further controlled using AxisLayoutOptions
* @warning Calculating layouts can get increasingly expensive for large
* amounts of child nodes being fit into a small space - while this should
* never prove a real performance concern as most layouts only have a few
* hundred children at the very most, be aware that you probably shouldn't
* call CCNode::updateLayout every frame for a menu with thousands of children
* @example
* auto menu = CCMenu::create();
* // The menu's children will be arranged horizontally, unless they overflow
* // the content size width in which case a new line will be inserted and
* // aligned to the left. The menu automatically will automatically grow in
* // height to fit all the rows
* menu->setLayout(
* RowLayout::create()
* ->setGap(10.f)
* ->setGrowCrossAxis(true)
* ->setAxisAlignment(AxisAlignment::Start)
* );
* menu->setContentSize({ 200.f, 0.f });
* menu->addChild(...);
* menu->updateLayout();
*/
class GEODE_DLL AxisLayout : public Layout {
protected:
class Impl;
std::unique_ptr<Impl> m_impl;
AxisLayout(Axis);
public:
/**
* Create a new AxisLayout. Note that this class is not automatically
* managed by default, so you must assign it to a CCNode or manually
* manage the memory yourself. See the chainable setters on AxisLayout for
* what options you can customize for the layout
* @param axis The direction of the layout
* @note For convenience, you can use the RowLayout and ColumnLayout
* classes, which are just thin wrappers over AxisLayout
* @returns Created AxisLayout
*/
static AxisLayout* create(Axis axis = Axis::Row);
virtual ~AxisLayout();
void apply(cocos2d::CCNode* on) override;
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
Axis getAxis() const;
AxisAlignment getAxisAlignment() const;
AxisAlignment getCrossAxisAlignment() const;
AxisAlignment getCrossAxisLineAlignment() const;
float getGap() const;
bool getAxisReverse() const;
bool getCrossAxisReverse() const;
bool getAutoScale() const;
bool getGrowCrossAxis() const;
bool getCrossAxisOverflow() const;
std::optional<float> getAutoGrowAxis() const;
float getDefaultMinScale() const;
float getDefaultMaxScale() const;
AxisLayout* setAxis(Axis axis);
/**
* Sets where to align the target node's children on the main axis (X-axis
* for Row, Y-axis for Column)
*/
AxisLayout* setAxisAlignment(AxisAlignment align);
/**
* Sets where to align the target node's children on the cross-axis (Y-axis
* for Row, X-axis for Column)
*/
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
/**
* Sets where to align the target node's children on the cross-axis for
* each row (Y-axis for Row, X-axis for Column)
*/
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
/**
* The spacing between the children of the node this layout applies to.
* Measured as the space between their edges, not centres. Does not apply
* on the main / cross axis if their alignment is AxisAlignment::Even
*/
AxisLayout* setGap(float gap);
/**
* Whether to reverse the direction of the children in this layout or not
*/
AxisLayout* setAxisReverse(bool reverse);
/**
* Whether to reverse the direction of the rows on the cross-axis or not
*/
AxisLayout* setCrossAxisReverse(bool reverse);
/**
* If enabled, then the layout may scale the target's children if they are
* about to overflow. Assumes that all the childrens' intended scale is 1
*/
AxisLayout* setAutoScale(bool enable);
/**
* If true, if the main axis overflows extra nodes will be placed on new
* rows/columns on the cross-axis
*/
AxisLayout* setGrowCrossAxis(bool expand);
/**
* If true, the cross-axis content size of the target node will be
* automatically adjusted to fit the children
*/
AxisLayout* setCrossAxisOverflow(bool allow);
/**
* If not `std::nullopt`, then the axis will be automatically extended to
* fit all items in a single row whose minimum length is the specified.
* Useful for scrollable list layer contents
*/
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
/**
* Set the default minimum/maximum scales for nodes in the layout
*/
AxisLayout* setDefaultScaleLimits(float min, float max);
};
/**
* Simple layout for arranging nodes in a row (horizontal line)
*/
class GEODE_DLL RowLayout : public AxisLayout {
protected:
RowLayout();
public:
/**
* Create a new RowLayout. See the chainable setters on RowLayout for
* what options you can customize for the layout
* @returns Created RowLayout
*/
static RowLayout* create();
};
/**
* Simple layout for arranging nodes in a column (vertical line)
*/
class GEODE_DLL ColumnLayout : public AxisLayout {
protected:
ColumnLayout();
public:
/**
* Create a new ColumnLayout. See the chainable setters on RowLayout for
* what options you can customize for the layout
* @returns Created ColumnLayout
*/
static ColumnLayout* create();
};
/**
* The relative position of a node to its parent in an AnchorLayout
*/
enum class Anchor {
Center,
TopLeft,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left,
};
/**
* Options for customizing a node's position in an AnchorLayout
*/
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
protected:
Anchor m_anchor = Anchor::Center;
cocos2d::CCPoint m_offset = cocos2d::CCPointZero;
public:
static AnchorLayoutOptions* create();
Anchor getAnchor() const;
cocos2d::CCPoint getOffset() const;
AnchorLayoutOptions* setAnchor(Anchor anchor);
AnchorLayoutOptions* setOffset(cocos2d::CCPoint const& offset);
};
/**
* A layout for positioning nodes at specific positions relative to their
* parent's content size. See `Anchor` for available anchoring options. Useful
* for example for popups, where a popup using `AnchorLayout` can be
* automatically resized without needing to manually shuffle nodes around
*/
class GEODE_DLL AnchorLayout : public Layout {
public:
static AnchorLayout* create();
void apply(cocos2d::CCNode* on) override;
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
/**
* Get a position according to anchoring rules, with the same algorithm as
* `AnchorLayout` uses to position its nodes
* @param in The node whose content size to use as a reference
* @param anchor The anchor position
* @param offset Offset from the anchor
* @returns A position in `in` for the anchored and offsetted location
*/
static cocos2d::CCPoint getAnchoredPosition(cocos2d::CCNode* in, Anchor anchor, cocos2d::CCPoint const& offset);
};
/**
* A layout for automatically copying the content size of a node to other nodes.
* Basically main use case is for FLAlertLayers (setting the size of the
* background and `m_buttonMenu` based on `m_mainLayer`)
*/
class GEODE_DLL CopySizeLayout : public AnchorLayout {
protected:
cocos2d::CCArray* m_targets;
public:
static CopySizeLayout* create();
virtual ~CopySizeLayout();
/**
* Add a target to be automatically resized. Any targets' layouts will
* also be updated when this layout is updated
*/
CopySizeLayout* add(cocos2d::CCNode* target);
/**
* Remove a target from being automatically resized
*/
CopySizeLayout* remove(cocos2d::CCNode* target);
void apply(cocos2d::CCNode* in) override;
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
};
#pragma warning(pop)
}

View file

@ -4,6 +4,7 @@
#include <Geode/binding/FLAlertLayer.hpp>
#include <Geode/utils/MiniFunction.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/ui/Layout.hpp>
namespace geode {
template <class... InitArgs>
@ -104,7 +105,7 @@ namespace geode {
m_mainLayer->setPosition(winSize / 2);
m_mainLayer->setContentSize(m_size);
m_mainLayer->setLayout(
cocos2d::CopySizeLayout::create()
geode::CopySizeLayout::create()
->add(m_buttonMenu)
->add(m_bgSprite)
);
@ -119,7 +120,7 @@ namespace geode {
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
);
if (dynamic) {
m_buttonMenu->addChildAtPosition(m_closeBtn, cocos2d::Anchor::TopLeft, { 3.f, -3.f });
m_buttonMenu->addChildAtPosition(m_closeBtn, geode::Anchor::TopLeft, { 3.f, -3.f });
}
else {
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f);
@ -137,14 +138,6 @@ namespace geode {
}
protected:
[[deprecated("Use Popup::initAnchored instead, as it has more reasonable menu and layer content sizes")]]
bool init(
float width, float height, InitArgs... args, char const* bg = "GJ_square01.png",
cocos2d::CCRect bgRect = { 0, 0, 80, 80 }
) {
return this->initBase(width, height, std::forward<InitArgs>(args)..., bg, bgRect, false);
}
/**
* Init with AnchorLayout and the content size of `m_buttonMenu` and
* `m_bgSprite` being tied to the size of `m_mainLayer` (rather than
@ -185,7 +178,7 @@ namespace geode {
m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font);
m_title->setZOrder(2);
if (m_dynamic) {
m_mainLayer->addChildAtPosition(m_title, cocos2d::Anchor::Top, ccp(0, -offset));
m_mainLayer->addChildAtPosition(m_title, geode::Anchor::Top, ccp(0, -offset));
}
else {
auto winSize = cocos2d::CCDirector::get()->getWinSize();

View file

@ -1,8 +1,8 @@
#pragma once
#include "CCNode.h"
#include <cocos2d.h>
NS_CC_BEGIN
namespace geode {
#pragma warning(push)
#pragma warning(disable: 4275)
@ -22,7 +22,7 @@ NS_CC_BEGIN
* @note If you want to specify a minimum width for a SpacerNode, add
* AxisLayoutOptions for it and use setLength
*/
class GEODE_DLL SpacerNode : public CCNode {
class GEODE_DLL SpacerNode : public cocos2d::CCNode {
protected:
size_t m_grow;
@ -61,9 +61,9 @@ public:
*/
class GEODE_DLL SpacerNodeChild : public SpacerNode {
protected:
CCNode* m_child = nullptr;
cocos2d::CCNode* m_child = nullptr;
bool init(CCNode* child, size_t grow);
bool init(cocos2d::CCNode* child, size_t grow);
public:
/**
@ -73,11 +73,11 @@ public:
* factors (akin to CSS flew grow)
* @param grow The grow factor for this node. Default is 1
*/
static SpacerNodeChild* create(CCNode* child, size_t grow = 1);
static SpacerNodeChild* create(cocos2d::CCNode* child, size_t grow = 1);
void setContentSize(CCSize const& size) override;
void setContentSize(cocos2d::CCSize const& size) override;
};
#pragma warning(pop)
NS_CC_END
}

View file

@ -78,226 +78,6 @@ namespace geode {
struct JsonMaybeObject;
struct JsonMaybeValue;
struct GEODE_DLL
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
JsonMaybeSomething {
protected:
JsonChecker& m_checker;
matjson::Value& m_json;
std::string m_hierarchy;
bool m_hasValue;
friend struct JsonMaybeObject;
friend struct JsonMaybeValue;
void setError(std::string const& error);
public:
matjson::Value& json();
JsonMaybeSomething(
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
);
bool isError() const;
std::string getError() const;
operator bool() const;
};
struct GEODE_DLL
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
JsonMaybeValue : public JsonMaybeSomething {
bool m_inferType = true;
JsonMaybeValue(
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
);
JsonMaybeSomething& self();
template <matjson::Type T>
JsonMaybeValue& as() {
if (this->isError()) return *this;
if (!jsonConvertibleTo(self().m_json.type(), T)) {
this->setError(
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
"\", expected \"" + jsonValueTypeToString(T) + "\""
);
}
m_inferType = false;
return *this;
}
JsonMaybeValue& array();
template <matjson::Type... T>
JsonMaybeValue& asOneOf() {
if (this->isError()) return *this;
bool isOneOf = (... || jsonConvertibleTo(self().m_json.type(), T));
if (!isOneOf) {
this->setError(
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
"\", expected one of \"" + (jsonValueTypeToString(T), ...) + "\""
);
}
m_inferType = false;
return *this;
}
template <class T>
bool is() {
if (this->isError()) return false;
return self().m_json.is<T>();
}
template <class T>
JsonMaybeValue& validate(JsonValueValidator<T> validator) {
if (this->isError()) return *this;
if (self().m_json.is<T>()) {
if (!validator(self().m_json.as<T>())) {
this->setError(self().m_hierarchy + ": Invalid value format");
}
}
else {
this->setError(
self().m_hierarchy + ": Invalid type \"" +
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
);
}
return *this;
}
template <class T>
JsonMaybeValue& inferType() {
if (this->isError() || !m_inferType) return *this;
return this->as<getJsonType<T>()>();
}
template <class T>
JsonMaybeValue& intoRaw(T& target) {
if (this->isError()) return *this;
target = self().m_json;
return *this;
}
template <class T>
JsonMaybeValue& into(T& target) {
return this->intoAs<T, T>(target);
}
template <class T>
JsonMaybeValue& into(std::optional<T>& target) {
return this->intoAs<T, std::optional<T>>(target);
}
template <class A, class T>
JsonMaybeValue& intoAs(T& target) {
this->inferType<A>();
if (this->isError()) return *this;
if (self().m_json.is<A>()) {
try {
target = self().m_json.as<A>();
}
catch(matjson::JsonException const& e) {
this->setError(
self().m_hierarchy + ": Error parsing JSON: " + std::string(e.what())
);
}
}
else {
this->setError(
self().m_hierarchy + ": Invalid type \"" +
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
);
}
return *this;
}
template <class T>
T get() {
this->inferType<T>();
if (this->isError()) return T();
if (self().m_json.is<T>()) {
return self().m_json.as<T>();
}
return T();
}
JsonMaybeObject obj();
template <class T>
struct Iterator {
std::vector<T> m_values;
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
iterator begin() {
return m_values.begin();
}
iterator end() {
return m_values.end();
}
const_iterator begin() const {
return m_values.begin();
}
const_iterator end() const {
return m_values.end();
}
};
JsonMaybeValue at(size_t i);
Iterator<JsonMaybeValue> iterate();
Iterator<std::pair<std::string, JsonMaybeValue>> items();
};
struct
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
GEODE_DLL JsonMaybeObject : JsonMaybeSomething {
std::set<std::string> m_knownKeys;
JsonMaybeObject(
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
);
JsonMaybeSomething& self();
void addKnownKey(std::string const& key);
matjson::Value& json();
JsonMaybeValue emptyValue();
JsonMaybeValue has(std::string const& key);
JsonMaybeValue needs(std::string const& key);
void checkUnknownKeys();
};
struct
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
GEODE_DLL JsonChecker {
std::variant<std::monostate, std::string> m_result;
matjson::Value& m_json;
JsonChecker(matjson::Value& json);
bool isError() const;
std::string getError() const;
JsonMaybeValue root(std::string const& hierarchy);
};
class GEODE_DLL JsonExpectedValue final {
protected:
class Impl;

View file

@ -186,8 +186,6 @@ namespace geode {
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
}
[[deprecated("Use toNonVString or toVString instead")]]
std::string toString(bool includeTag = true) const;
std::string toVString(bool includeTag = true) const;
std::string toNonVString(bool includeTag = true) const;

View file

@ -615,18 +615,6 @@ namespace geode::cocos {
return static_cast<T*>(x->getChildren()->objectAtIndex(i));
}
/**
* Get nth child that is a given type. Checks bounds.
* @returns Child at index cast to the given type,
* or nullptr if index exceeds bounds
*/
template <class Type = cocos2d::CCNode>
[[deprecated("Use CCNode::getChildByType instead")]]
static Type* getChildOfType(cocos2d::CCNode* node, int index) {
return node->getChildByType<Type>(index);
}
/**
* Return a node, or create a default one if it's
* nullptr. Syntactic sugar function
@ -856,30 +844,6 @@ namespace geode::cocos {
return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f};
}
[[deprecated("This function may have unintended behavior, use cc3bFromHexString or manually expand the color instead")]]
constexpr cocos2d::ccColor3B cc3x(int hexValue) {
if (hexValue <= 0xf)
return cocos2d::ccColor3B{
static_cast<GLubyte>(hexValue * 17),
static_cast<GLubyte>(hexValue * 17),
static_cast<GLubyte>(hexValue * 17)};
if (hexValue <= 0xff)
return cocos2d::ccColor3B{
static_cast<GLubyte>(hexValue),
static_cast<GLubyte>(hexValue),
static_cast<GLubyte>(hexValue)};
if (hexValue <= 0xfff)
return cocos2d::ccColor3B{
static_cast<GLubyte>((hexValue >> 8 & 0xf) * 17),
static_cast<GLubyte>((hexValue >> 4 & 0xf) * 17),
static_cast<GLubyte>((hexValue >> 0 & 0xf) * 17)};
else
return cocos2d::ccColor3B{
static_cast<GLubyte>(hexValue >> 16 & 0xff),
static_cast<GLubyte>(hexValue >> 8 & 0xff),
static_cast<GLubyte>(hexValue >> 0 & 0xff)};
}
/**
* Parse a ccColor3B from a hexadecimal string. The string may contain
* a leading '#'

View file

@ -17,15 +17,6 @@
namespace geode {
using ByteVector = std::vector<uint8_t>;
// todo in v4: remove this
template <typename T>
[[deprecated("Use geode::toBytes instead")]]
ByteVector toByteArray(T const& a) {
ByteVector out;
out.resize(sizeof(T));
std::memcpy(out.data(), &a, sizeof(T));
return out;
}
template <typename T>
ByteVector toBytes(T const& a) {
ByteVector out;

View file

@ -1026,14 +1026,6 @@ std::optional<AxisAlignment> AxisLayoutOptions::getCrossAxisAlignment() const {
return m_impl->m_crossAxisAlignment;
}
AxisLayoutOptions* AxisLayoutOptions::setMaxScale(float scale) {
m_impl->m_scaleLimits.second = scale;
return this;
}
AxisLayoutOptions* AxisLayoutOptions::setMinScale(float scale) {
m_impl->m_scaleLimits.first = scale;
return this;
}
AxisLayoutOptions* AxisLayoutOptions::setScaleLimits(std::optional<float> min, std::optional<float> max) {
m_impl->m_scaleLimits = { min, max };
return this;

View file

@ -62,10 +62,6 @@ public:
return meta;
}
FieldContainer* getFieldContainer() {
return nullptr;
}
FieldContainer* getFieldContainer(char const* forClass) {
if (!m_classFieldContainers.count(forClass)) {
m_classFieldContainers[forClass] = new FieldContainer();
@ -104,11 +100,6 @@ size_t modifier::getFieldIndexForClass(char const* name) {
return s_nextIndex[name]++;
}
// not const because might modify contents
FieldContainer* CCNode::getFieldContainer() {
return GeodeNodeMetadata::set(this)->getFieldContainer();
}
FieldContainer* CCNode::getFieldContainer(char const* forClass) {
return GeodeNodeMetadata::set(this)->getFieldContainer(forClass);
}
@ -455,13 +446,4 @@ void CCNode::updateAnchoredPosition(Anchor anchor, CCPoint const& offset, CCPoin
}
}
#ifdef GEODE_EXPORTING
void CCNode::setAttribute(std::string const& attr, matjson::Value const& value) {}
std::optional<matjson::Value> CCNode::getAttributeInternal(std::string const& attr) {
return std::nullopt;
}
#endif
#pragma warning(pop)

View file

@ -7,7 +7,6 @@
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Log.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <loader/LogImpl.hpp>
@ -30,8 +29,7 @@ $on_mod(Loaded) {
std::vector<matjson::Value> res;
auto args = *event->messageData;
JsonChecker checker(args);
auto root = checker.root("[ipc/list-mods]").obj();
auto root = checkJson(args, "[ipc/list-mods]");
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();

View file

@ -20,10 +20,6 @@ std::string Mod::getName() const {
return m_impl->getName();
}
std::string Mod::getDeveloper() const {
return m_impl->getDevelopers().empty() ? "" : m_impl->getDevelopers().front();
}
std::vector<std::string> Mod::getDevelopers() const {
return m_impl->getDevelopers();
}
@ -101,9 +97,6 @@ std::vector<Mod*> Mod::getDependants() const {
}
#endif
std::optional<VersionInfo> Mod::hasAvailableUpdate() const {
return std::nullopt;
}
Mod::CheckUpdatesTask Mod::checkUpdates() const {
return server::checkUpdates(this).map(
[](auto* result) -> Mod::CheckUpdatesTask::Value {
@ -159,24 +152,10 @@ bool Mod::hasSetting(std::string_view const key) const {
return m_impl->hasSetting(key);
}
std::optional<Setting> Mod::getSettingDefinition(std::string_view const key) const {
return m_impl->m_settings->getLegacyDefinition(std::string(key));
}
SettingValue* Mod::getSetting(std::string_view const key) const {
return m_impl->m_settings->getLegacy(std::string(key)).get();
}
std::shared_ptr<SettingV3> Mod::getSettingV3(std::string_view const key) const {
return m_impl->m_settings->get(std::string(key));
}
void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value) {
auto reg = m_impl->m_settings->registerLegacyCustomSetting(key, std::move(value));
if (!reg) {
log::error("Unable to register custom setting: {}", reg.unwrapErr());
}
}
Result<> Mod::registerCustomSettingType(std::string_view type, SettingGenerator generator) {
return m_impl->m_settings->registerCustomSettingType(type, generator);
}

View file

@ -528,18 +528,6 @@ std::vector<ModMetadata::Incompatibility> ModMetadata::getIncompatibilities() co
std::vector<std::string> ModMetadata::getSpritesheets() const {
return m_impl->m_spritesheets;
}
std::vector<std::pair<std::string, Setting>> ModMetadata::getSettings() const {
std::vector<std::pair<std::string, Setting>> res;
for (auto [key, sett] : m_impl->m_settings) {
auto checker = JsonChecker(sett);
auto value = checker.root("");
auto legacy = Setting::parse(key, m_impl->m_id, value);
if (!checker.isError() && legacy.isOk()) {
res.push_back(std::make_pair(key, *legacy));
}
}
return res;
}
std::vector<std::pair<std::string, matjson::Value>> ModMetadata::getSettingsV3() const {
return m_impl->m_settings;
}

View file

@ -13,8 +13,6 @@ 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 },
@ -202,19 +200,6 @@ Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, s
if (!m_impl->settings.count(id)) {
return Err("No such setting '{}' in mod {}", id, m_impl->modID);
}
auto& sett = m_impl->settings.at(id);
if (auto custom = typeinfo_pointer_cast<LegacyCustomSettingV3>(sett.v3)) {
if (!custom->getValue()) {
custom->setValue(std::move(ptr));
m_impl->loadSettingValueFromSave(id);
}
else {
return Err("Setting '{}' in mod {} has already been registed", id, m_impl->modID);
}
}
else {
return Err("Setting '{}' in mod {} is not a legacy custom setting", id, m_impl->modID);
}
return Ok();
}
@ -244,33 +229,6 @@ std::shared_ptr<SettingV3> ModSettingsManager::get(std::string_view key) {
auto id = std::string(key);
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->settings.count(id)) {
return nullptr;
}
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);
return info.legacy;
}
return nullptr;
}
std::optional<Setting> ModSettingsManager::getLegacyDefinition(std::string_view key) {
if (auto s = this->get(key)) {
return s->convertToLegacy();
}
return std::nullopt;
}
bool ModSettingsManager::restartRequired() const {
return m_impl->restartRequired;

View file

@ -1,498 +0,0 @@
#include <ui/mods/settings/GeodeSettingNode.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/Setting.hpp>
#include <Geode/loader/SettingV3.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/utils/general.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <regex>
using namespace geode::prelude;
template<class T>
static void parseCommon(T& sett, JsonMaybeObject& obj) {
obj.has("name").into(sett.name);
obj.has("description").into(sett.description);
if (auto defValue = obj.needs("default")) {
// Platform-specific default value
if (defValue.is<matjson::Object>()) {
auto def = defValue.obj();
if (auto plat = def.has(PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))) {
plat.into(sett.defaultValue);
}
else {
defValue.into(sett.defaultValue);
}
}
else {
defValue.into(sett.defaultValue);
}
}
}
Result<BoolSetting> BoolSetting::parse(JsonMaybeObject& obj) {
BoolSetting sett;
parseCommon(sett, obj);
return Ok(sett);
}
Result<IntSetting> IntSetting::parse(JsonMaybeObject& obj) {
IntSetting sett;
parseCommon(sett, obj);
obj.has("min").into(sett.min);
obj.has("max").into(sett.max);
if (auto controls = obj.has("control").obj()) {
controls.has("arrows").into(sett.controls.arrows);
controls.has("big-arrows").into(sett.controls.bigArrows);
controls.has("arrow-step").into(sett.controls.arrowStep);
controls.has("big-arrow-step").into(sett.controls.bigArrowStep);
controls.has("slider").into(sett.controls.slider);
controls.has("slider-step").into(sett.controls.sliderStep);
controls.has("input").into(sett.controls.input);
}
return Ok(sett);
}
Result<FloatSetting> FloatSetting::parse(JsonMaybeObject& obj) {
FloatSetting sett;
parseCommon(sett, obj);
obj.has("min").into(sett.min);
obj.has("max").into(sett.max);
if (auto controls = obj.has("control").obj()) {
controls.has("arrows").into(sett.controls.arrows);
controls.has("big-arrows").into(sett.controls.bigArrows);
controls.has("arrow-step").into(sett.controls.arrowStep);
controls.has("big-arrow-step").into(sett.controls.bigArrowStep);
controls.has("slider").into(sett.controls.slider);
controls.has("slider-step").into(sett.controls.sliderStep);
controls.has("input").into(sett.controls.input);
}
return Ok(sett);
}
Result<StringSetting> StringSetting::parse(JsonMaybeObject& obj) {
StringSetting sett;
parseCommon(sett, obj);
obj.has("match").into(sett.controls->match);
obj.has("filter").into(sett.controls->filter);
obj.has("one-of").into(sett.controls->options);
return Ok(sett);
}
Result<FileSetting> FileSetting::parse(JsonMaybeObject& obj) {
FileSetting sett;
parseCommon(sett, obj);
if (auto controls = obj.has("control").obj()) {
for (auto& item : controls.has("filters").iterate()) {
if (auto iobj = item.obj()) {
Filter filter;
iobj.has("description").into(filter.description);
std::vector<matjson::Value> files;
iobj.has("files").into(files);
for (auto& i : files) {
filter.files.insert(i.as<std::string>());
}
sett.controls.filters.push_back(filter);
}
}
}
return Ok(sett);
}
Result<ColorSetting> ColorSetting::parse(JsonMaybeObject& obj) {
ColorSetting sett;
parseCommon(sett, obj);
return Ok(sett);
}
Result<ColorAlphaSetting> ColorAlphaSetting::parse(JsonMaybeObject& obj) {
ColorAlphaSetting sett;
parseCommon(sett, obj);
return Ok(sett);
}
Result<Setting> Setting::parse(
std::string const& key,
std::string const& mod,
JsonMaybeValue& value
) {
auto sett = Setting();
sett.m_key = key;
sett.m_modID = mod;
if (auto obj = value.obj()) {
std::string type;
obj.needs("type").into(type);
if (type.size()) {
switch (hash(type.c_str())) {
case hash("bool"): {
GEODE_UNWRAP_INTO(sett.m_kind, BoolSetting::parse(obj));
} break;
case hash("int"): {
GEODE_UNWRAP_INTO(sett.m_kind, IntSetting::parse(obj));
} break;
case hash("float"): {
GEODE_UNWRAP_INTO(sett.m_kind, FloatSetting::parse(obj));
} break;
case hash("string"): {
GEODE_UNWRAP_INTO(sett.m_kind, StringSetting::parse(obj));
} break;
case hash("rgb"): case hash("color"): {
GEODE_UNWRAP_INTO(sett.m_kind, ColorSetting::parse(obj));
} break;
case hash("rgba"): {
GEODE_UNWRAP_INTO(sett.m_kind, ColorAlphaSetting::parse(obj));
} break;
case hash("path"): case hash("file"): {
GEODE_UNWRAP_INTO(sett.m_kind, FileSetting::parse(obj));
} break;
case hash("custom"): {
sett.m_kind = CustomSetting {
.json = std::make_shared<ModJson>(obj.json())
};
// skip checking unknown keys
return Ok(sett);
} break;
default: return Err("Unknown setting type \"" + type + "\"");
}
}
// this is handled before the setting is parsed
obj.addKnownKey("platforms");
obj.checkUnknownKeys();
}
// if the type wasn't an object or a string, the JsonChecker that gave the
// JsonMaybeValue will fail eventually so we can continue on
return Ok(sett);
}
Setting::Setting(
std::string const& key,
std::string const& mod,
SettingKind const& kind
) : m_key(key), m_modID(mod), m_kind(kind) {}
std::unique_ptr<SettingValue> Setting::createDefaultValue() const {
return std::visit(makeVisitor {
[&](BoolSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<BoolSettingValue>(m_key, m_modID, sett);
},
[&](IntSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<IntSettingValue>(m_key, m_modID, sett);
},
[&](FloatSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<FloatSettingValue>(m_key, m_modID, sett);
},
[&](StringSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<StringSettingValue>(m_key, m_modID, sett);
},
[&](FileSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<FileSettingValue>(m_key, m_modID, sett);
},
[&](ColorSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<ColorSettingValue>(m_key, m_modID, sett);
},
[&](ColorAlphaSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<ColorAlphaSettingValue>(m_key, m_modID, sett);
},
[&](auto const& sett) -> std::unique_ptr<SettingValue> {
return nullptr;
},
}, m_kind);
}
bool Setting::isCustom() const {
return std::holds_alternative<CustomSetting>(m_kind);
}
std::string Setting::getDisplayName() const {
return std::visit(makeVisitor {
[&](CustomSetting const& sett) {
return std::string();
},
[&](auto const& sett) {
return sett.name.value_or(m_key);
},
}, m_kind);
}
std::optional<std::string> Setting::getDescription() const {
return std::visit(makeVisitor {
[&](CustomSetting const& sett) -> std::optional<std::string> {
return std::nullopt;
},
[&](auto const& sett) {
return sett.description;
},
}, m_kind);
}
std::string Setting::getModID() const {
return m_modID;
}
// StringSetting
StringSetting::StringSetting() : name(), description(), defaultValue(), controls(new Data()) {}
StringSetting::StringSetting(StringSetting const& other) : name(other.name), description(other.description),
defaultValue(other.defaultValue), controls(new Data(*other.controls)) {}
StringSetting::StringSetting(StringSetting&& other) noexcept : name(std::move(other.name)), description(std::move(other.description)),
defaultValue(std::move(other.defaultValue)), controls(std::move(other.controls)) {}
StringSetting& StringSetting::operator=(StringSetting const& other) {
name = other.name;
description = other.description;
defaultValue = other.defaultValue;
*controls = *other.controls;
return *this;
}
StringSetting& StringSetting::operator=(StringSetting&& other) noexcept {
name = std::move(other.name);
description = std::move(other.description);
defaultValue = std::move(other.defaultValue);
controls = std::move(other.controls);
return *this;
}
StringSetting::~StringSetting() = default;
// SettingValue
SettingValue::SettingValue(std::string const& key, std::string const& mod)
: m_key(key), m_modID(mod) {}
std::string SettingValue::getKey() const {
return m_key;
}
std::string SettingValue::getModID() const {
return m_modID;
}
void SettingValue::valueChanged() {
if (auto mod = Loader::get()->getLoadedMod(m_modID)) {
if (auto sett = mod->getSettingV3(m_key)) {
sett->markChanged();
}
}
}
// GeodeSettingValue & SettingValueSetter specializations
#define IMPL_NODE_AND_SETTERS(type_) \
template<> \
SettingNode* GeodeSettingValue< \
type_##Setting \
>::createNode(float width) { \
return type_##SettingNode::create(this, width); \
} \
template<> \
typename GeodeSettingValue<type_##Setting>::ValueType \
GeodeSettingValue<type_##Setting>::getValue() const { \
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
return setting->getValue(); \
} \
} \
return m_value; \
} \
template<> \
void GeodeSettingValue< \
type_##Setting \
>::setValue(ValueType const& value) { \
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
return setting->setValue(value); \
} \
} \
} \
template<> \
Result<> GeodeSettingValue< \
type_##Setting \
>::validate(ValueType const& value) const { \
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
return setting->isValid(value); \
} \
} \
return Ok(); \
} \
template<> \
typename type_##Setting::ValueType SettingValueSetter< \
typename type_##Setting::ValueType \
>::get(SettingValue* setting) { \
using S = typename SettingTypeForValueType<typename type_##Setting::ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(setting->getModID())) { \
if (auto sett = typeinfo_pointer_cast<S>(mod->getSettingV3(setting->getKey()))) { \
return sett->getValue(); \
} \
} \
return typename type_##Setting::ValueType(); \
} \
template<> \
void SettingValueSetter< \
typename type_##Setting::ValueType \
>::set( \
SettingValue* setting, \
typename type_##Setting::ValueType const& value \
) { \
using S = typename SettingTypeForValueType<typename type_##Setting::ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(setting->getModID())) { \
if (auto sett = typeinfo_pointer_cast<S>(mod->getSettingV3(setting->getKey()))) { \
return sett->setValue(value); \
} \
} \
}
#define IMPL_TO_VALID(type_) \
template<> \
typename GeodeSettingValue<type_##Setting>::Valid \
GeodeSettingValue<type_##Setting>::toValid( \
typename type_##Setting::ValueType const& value \
) const
// instantiate values
namespace geode {
template class GeodeSettingValue<BoolSetting>;
template class GeodeSettingValue<IntSetting>;
template class GeodeSettingValue<FloatSetting>;
template class GeodeSettingValue<StringSetting>;
template class GeodeSettingValue<FileSetting>;
template class GeodeSettingValue<ColorSetting>;
template class GeodeSettingValue<ColorAlphaSetting>;
}
IMPL_TO_VALID(Bool) {
return { value, std::nullopt };
}
IMPL_TO_VALID(Int) {
if (m_definition.min && value < m_definition.min) {
return { m_definition.min.value(), fmt::format(
"Value must be more than or equal to {}",
m_definition.min.value()
) };
}
if (m_definition.max && value > m_definition.max) {
return { m_definition.max.value(), fmt::format(
"Value must be less than or equal to {}",
m_definition.max.value()
) };
}
return { value, std::nullopt };
}
IMPL_TO_VALID(Float) {
if (m_definition.min && value < m_definition.min) {
return { m_definition.min.value(), fmt::format(
"Value must be more than or equal to {}",
m_definition.min.value()
) };
}
if (m_definition.max && value > m_definition.max) {
return { m_definition.max.value(), fmt::format(
"Value must be less than or equal to {}",
m_definition.max.value()
) };
}
return { value, std::nullopt };
}
IMPL_TO_VALID(String) {
if (m_definition.controls->match) {
if (!std::regex_match(value, std::regex{m_definition.controls->match.value()})) {
return {
m_definition.defaultValue,
fmt::format(
"Value must match regex {}",
m_definition.controls->match.value()
)
};
}
}
else if (m_definition.controls->options) {
if (std::find(
m_definition.controls->options.value().begin(),
m_definition.controls->options.value().end(),
value
) == m_definition.controls->options.value().end()) {
return {
m_definition.defaultValue,
fmt::format(
"Value must be one of {}",
fmt::join(m_definition.controls->options.value(), ", ")
)
};
}
}
return { value, std::nullopt };
}
IMPL_TO_VALID(File) {
return { value, std::nullopt };
}
IMPL_TO_VALID(Color) {
return { value, std::nullopt };
}
IMPL_TO_VALID(ColorAlpha) {
return { value, std::nullopt };
}
IMPL_NODE_AND_SETTERS(Bool);
IMPL_NODE_AND_SETTERS(Int);
IMPL_NODE_AND_SETTERS(Float);
IMPL_NODE_AND_SETTERS(String);
IMPL_NODE_AND_SETTERS(File);
IMPL_NODE_AND_SETTERS(Color);
IMPL_NODE_AND_SETTERS(ColorAlpha);
// instantiate value setters
namespace geode {
template struct SettingValueSetter<typename BoolSetting::ValueType>;
template struct SettingValueSetter<typename IntSetting::ValueType>;
template struct SettingValueSetter<typename FloatSetting::ValueType>;
template struct SettingValueSetter<typename StringSetting::ValueType>;
template struct SettingValueSetter<typename FileSetting::ValueType>;
template struct SettingValueSetter<typename ColorSetting::ValueType>;
template struct SettingValueSetter<typename ColorAlphaSetting::ValueType>;
}
// SettingChangedEvent
SettingChangedEvent::SettingChangedEvent(Mod* mod, SettingValue* value)
: mod(mod), value(value) {}
// SettingChangedFilter
ListenerResult SettingChangedFilter::handle(
utils::MiniFunction<Callback> fn, SettingChangedEvent* event
) {
if (m_modID == event->mod->getID() &&
(!m_targetKey || m_targetKey.value() == event->value->getKey())
) {
fn(event->value);
}
return ListenerResult::Propagate;
}
SettingChangedFilter::SettingChangedFilter(
std::string const& modID,
std::optional<std::string> const& settingKey
) : m_modID(modID), m_targetKey(settingKey) {}

View file

@ -1,25 +0,0 @@
#include <Geode/loader/SettingNode.hpp>
#include <Geode/utils/cocos.hpp>
using namespace geode::prelude;
void SettingNode::dispatchChanged() {
if (m_delegate) {
m_delegate->settingValueChanged(this);
}
}
void SettingNode::dispatchCommitted() {
if (m_delegate) {
m_delegate->settingValueCommitted(this);
}
}
bool SettingNode::init(SettingValue* value) {
m_value = value;
return true;
}
void SettingNode::setDelegate(SettingNodeDelegate* delegate) {
m_delegate = delegate;
}

View file

@ -1,5 +1,4 @@
#include "SettingNodeV3.hpp"
#include <Geode/loader/SettingNode.hpp>
#include <Geode/utils/ColorProvider.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/loader/Dirs.hpp>

View file

@ -1,7 +1,6 @@
#pragma once
#include <Geode/loader/SettingV3.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
#include <Geode/binding/ColorChannelSprite.hpp>
#include <Geode/binding/Slider.hpp>
@ -314,25 +313,3 @@ public:
bool hasNonDefaultValue() const override;
void onResetToDefault() override;
};
// If these classes do get exposed in headers,
// LegacyCustomSettingToV3Node SHOULD NOT BE EXPOSED!!!!!! DO NOT DO THAT!!!!
class LegacyCustomSettingToV3Node : public SettingNodeV3, public SettingNodeDelegate {
protected:
SettingNode* m_original;
bool init(std::shared_ptr<LegacyCustomSettingV3> original, float width);
void onCommit() override;
void settingValueChanged(SettingNode*) override;
void settingValueCommitted(SettingNode*) override;
public:
static LegacyCustomSettingToV3Node* create(std::shared_ptr<LegacyCustomSettingV3> original, float width);
bool hasUncommittedChanges() const override;
bool hasNonDefaultValue() const override;
void onResetToDefault() override;
};

View file

@ -1,5 +1,4 @@
#include <Geode/loader/SettingV3.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/loader/ModSettingsManager.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/utils/string.hpp>
@ -574,19 +573,12 @@ void SettingV3::markChanged() {
}
SettingChangedEventV3(shared_from_this()).post();
if (manager) {
// TODO: v4
// Use ModSettingsManager rather than convertToLegacyValue since it
// caches the result and we want to have that for performance
SettingChangedEvent(this->getMod(), manager->getLegacy(this->getKey()).get()).post();
// SettingChangedEvent(this->getMod(), manager->getLegacy(this->getKey()).get()).post();
}
}
std::optional<Setting> SettingV3::convertToLegacy() const {
return std::nullopt;
}
std::optional<std::shared_ptr<SettingValue>> SettingV3::convertToLegacyValue() const {
return std::nullopt;
}
class TitleSettingV3::Impl final {
public:
};
@ -666,15 +658,6 @@ bool LegacyCustomSettingV3::isDefaultValue() const {
}
void LegacyCustomSettingV3::reset() {}
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>> LegacyCustomSettingV3::convertToLegacyValue() const {
return m_impl->legacyValue ? std::optional(m_impl->legacyValue) : std::nullopt;
}
class BoolSettingV3::Impl final {
public:
};
@ -699,17 +682,6 @@ SettingNodeV3* BoolSettingV3::createNode(float width) {
);
}
std::optional<Setting> BoolSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(BoolSetting {
.name = this->getName(),
.description = this->getDescription(),
.defaultValue = this->getDefaultValue(),
}));
}
std::optional<std::shared_ptr<SettingValue>> BoolSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class IntSettingV3::Impl final {
public:
std::optional<int64_t> minValue;
@ -819,28 +791,6 @@ SettingNodeV3* IntSettingV3::createNode(float width) {
);
}
std::optional<Setting> IntSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(IntSetting {
.name = this->getName(),
.description = this->getDescription(),
.defaultValue = this->getDefaultValue(),
.min = this->getMinValue(),
.max = this->getMaxValue(),
.controls = {
.arrows = this->isArrowsEnabled(),
.bigArrows = this->isBigArrowsEnabled(),
.arrowStep = this->getArrowStepSize(),
.bigArrowStep = this->getBigArrowStepSize(),
.slider = this->isSliderEnabled(),
.sliderStep = this->getSliderSnap(),
.input = this->isInputEnabled(),
},
}));
}
std::optional<std::shared_ptr<SettingValue>> IntSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class FloatSettingV3::Impl final {
public:
std::optional<double> minValue;
@ -948,27 +898,6 @@ SettingNodeV3* FloatSettingV3::createNode(float width) {
);
}
std::optional<Setting> FloatSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(FloatSetting {
.name = this->getName(),
.description = this->getDescription(),
.defaultValue = this->getDefaultValue(),
.min = this->getMinValue(),
.max = this->getMaxValue(),
.controls = {
.arrows = this->isArrowsEnabled(),
.bigArrows = this->isBigArrowsEnabled(),
.arrowStep = static_cast<size_t>(this->getArrowStepSize()),
.bigArrowStep = static_cast<size_t>(this->getBigArrowStepSize()),
.slider = this->isSliderEnabled(),
.sliderStep = this->getSliderSnap(),
.input = this->isInputEnabled(),
},
}));
}
std::optional<std::shared_ptr<SettingValue>> FloatSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class StringSettingV3::Impl final {
public:
@ -1026,20 +955,6 @@ SettingNodeV3* StringSettingV3::createNode(float width) {
);
}
std::optional<Setting> StringSettingV3::convertToLegacy() const {
auto setting = StringSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
setting.controls->filter = this->getAllowedCharacters();
setting.controls->match = this->getRegexValidator();
setting.controls->options = this->getEnumOptions();
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> StringSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class FileSettingV3::Impl final {
public:
bool folder = false;
@ -1153,18 +1068,6 @@ SettingNodeV3* FileSettingV3::createNode(float width) {
);
}
std::optional<Setting> FileSettingV3::convertToLegacy() const {
auto setting = FileSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
setting.controls.filters = this->getFilters().value_or(std::vector<utils::file::FilePickOptions::Filter>());
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> FileSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class Color3BSettingV3::Impl final {
public:
};
@ -1189,17 +1092,6 @@ SettingNodeV3* Color3BSettingV3::createNode(float width) {
);
}
std::optional<Setting> Color3BSettingV3::convertToLegacy() const {
auto setting = ColorSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> Color3BSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class Color4BSettingV3::Impl final {
public:
};
@ -1223,14 +1115,3 @@ SettingNodeV3* Color4BSettingV3::createNode(float width) {
std::static_pointer_cast<Color4BSettingV3>(shared_from_this()), width
);
}
std::optional<Setting> Color4BSettingV3::convertToLegacy() const {
auto setting = ColorAlphaSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> Color4BSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}

View file

@ -102,9 +102,7 @@ void updater::downloadLatestLoaderResources() {
log::debug("Downloading latest resources", Loader::get()->getVersion().toVString());
fetchLatestGithubRelease(
[](matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto root = checker.root("[]").obj();
auto root = checkJson(raw, "[]");
// find release asset
for (auto asset : root.needs("assets").iterate()) {
@ -206,9 +204,7 @@ void updater::downloadLoaderResources(bool useLatestRelease) {
RUNNING_REQUESTS.erase("@downloadLoaderResources");
if (response->ok()) {
if (auto ok = response->json()) {
auto json = ok.unwrap();
JsonChecker checker(json);
auto root = checker.root("[]").obj();
auto root = checkJson(ok.unwrap(), "[]");
// find release asset
for (auto asset : root.needs("assets").iterate()) {
@ -362,9 +358,7 @@ void updater::checkForLoaderUpdates() {
// Check for updates in the background
fetchLatestGithubRelease(
[](matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto root = checker.root("[]").obj();
auto root = checkJson(raw, "[]");
VersionInfo ver { 0, 0, 0 };
root.needs("tag_name").into(ver);

View file

@ -258,9 +258,7 @@ Result<ServerDateTime> ServerDateTime::parse(std::string const& str) {
}
Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto root = checker.root("ServerModVersion").obj();
auto root = checkJson(raw, "ServerModVersion");
auto res = ServerModVersion();
@ -356,9 +354,7 @@ Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
}
Result<ServerModReplacement> ServerModReplacement::parse(matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto root = checker.root("ServerModReplacement").obj();
auto root = checkJson(raw, "ServerModReplacement");
auto res = ServerModReplacement();
root.needs("id").into(res.id);
@ -371,9 +367,7 @@ Result<ServerModReplacement> ServerModReplacement::parse(matjson::Value const& r
}
Result<ServerModUpdate> ServerModUpdate::parse(matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto root = checker.root("ServerModUpdate").obj();
auto root = checkJson(raw, "ServerModUpdate");
auto res = ServerModUpdate();
@ -391,9 +385,7 @@ Result<ServerModUpdate> ServerModUpdate::parse(matjson::Value const& raw) {
}
Result<std::vector<ServerModUpdate>> ServerModUpdate::parseList(matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto payload = checker.root("ServerModUpdatesList").array();
auto payload = checkJson(raw, "ServerModUpdatesList");
std::vector<ServerModUpdate> list {};
for (auto item : payload.iterate()) {
@ -421,9 +413,7 @@ bool ServerModUpdate::hasUpdateForInstalledMod() const {
}
Result<ServerModMetadata> ServerModMetadata::parse(matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto root = checker.root("ServerModMetadata").obj();
auto root = checkJson(raw, "ServerModMetadata");
auto res = ServerModMetadata();
root.needs("id").into(res.id);
@ -501,9 +491,7 @@ std::string ServerModMetadata::formatDevelopersToString() const {
}
Result<ServerModsList> ServerModsList::parse(matjson::Value const& raw) {
auto json = raw;
JsonChecker checker(json);
auto payload = checker.root("ServerModsList").obj();
auto payload = checkJson(raw, "ServerModsList");
auto list = ServerModsList();
for (auto item : payload.needs("data").iterate()) {

View file

@ -3,7 +3,6 @@
#include "Geode/utils/VersionInfo.hpp"
#include <Geode/DefaultInclude.hpp>
#include <Geode/utils/web.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <chrono>
#include <matjson.hpp>
#include <vector>

View file

@ -1,7 +1,6 @@
#include "GeodeStyle.hpp"
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/ColorProvider.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/binding/ButtonSprite.hpp>
#include <Geode/ui/LoadingSpinner.hpp>

View file

@ -1,7 +1,7 @@
#include "ModDeveloperItem.hpp"
#include <Geode/cocos/base_nodes/CCNode.h>
#include <Geode/cocos/base_nodes/Layout.hpp>
#include <Geode/ui/Layout.hpp>
#include <Geode/cocos/cocoa/CCGeometry.h>
#include <Geode/cocos/label_nodes/CCLabelBMFont.h>
#include <Geode/cocos/platform/CCPlatformMacros.h>

View file

@ -1,7 +1,7 @@
#include "ModDeveloperList.hpp"
#include <Geode/cocos/base_nodes/CCNode.h>
#include <Geode/cocos/base_nodes/Layout.hpp>
#include <Geode/ui/Layout.hpp>
#include <Geode/cocos/cocoa/CCGeometry.h>
#include <Geode/cocos/platform/CCPlatformMacros.h>
#include <Geode/utils/cocos.hpp>

View file

@ -1,7 +1,7 @@
#include "ModProblemItem.hpp"
#include <Geode/cocos/base_nodes/CCNode.h>
#include <Geode/cocos/base_nodes/Layout.hpp>
#include <Geode/ui/Layout.hpp>
#include <Geode/cocos/cocoa/CCGeometry.h>
#include <Geode/cocos/label_nodes/CCLabelBMFont.h>
#include <Geode/cocos/platform/CCPlatformMacros.h>

View file

@ -1,7 +1,7 @@
#include "ModProblemList.hpp"
#include <Geode/cocos/base_nodes/CCNode.h>
#include <Geode/cocos/base_nodes/Layout.hpp>
#include <Geode/ui/Layout.hpp>
#include <Geode/cocos/cocoa/CCGeometry.h>
#include <Geode/cocos/platform/CCPlatformMacros.h>
#include <Geode/utils/cocos.hpp>

View file

@ -1,5 +1,5 @@
#include "ModErrorPopup.hpp"
#include "Geode/cocos/base_nodes/Layout.hpp"
#include <Geode/ui/Layout.hpp>
#include "ui/mods/GeodeStyle.hpp"
#include "ui/mods/list/ModProblemList.hpp"

View file

@ -1,558 +0,0 @@
#include "GeodeSettingNode.hpp"
#include "Geode/binding/FLAlertLayer.hpp"
#include "Geode/ui/Popup.hpp"
#include <Geode/binding/ButtonSprite.hpp>
#include <Geode/binding/CCTextInputNode.hpp>
#include <Geode/binding/ColorChannelSprite.hpp>
#include <Geode/binding/Slider.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Dirs.hpp>
#include <Geode/utils/general.hpp>
#include <charconv>
#include <clocale>
#include <filesystem>
// Helpers
template<class T>
static float valueToSlider(T const& setting, typename T::ValueType value) {
auto min = setting.min ? setting.min.value() : -100;
auto max = setting.max ? setting.max.value() : +100;
auto range = max - min;
return static_cast<float>(clamp(static_cast<double>(value - min) / range, 0.0, 1.0));
}
template<class T>
static typename T::ValueType valueFromSlider(T const& setting, float num) {
auto min = setting.min ? setting.min.value() : -100;
auto max = setting.max ? setting.max.value() : +100;
auto range = max - min;
auto value = static_cast<typename T::ValueType>(num * range + min);
if (auto step = setting.controls.sliderStep) {
value = static_cast<typename T::ValueType>(
round(value / step.value()) * step.value()
);
}
return value;
}
template<class C, class T>
TextInput* createInput(C* node, GeodeSettingValue<T>* setting, float width) {
auto input = TextInput::create(width / 2 - 70.f, "Text", "chatFont.fnt");
input->setPosition({
-(width / 2 - 70.f) / 2,
setting->castDefinition().controls.slider ? 5.f : 0.f
});
input->setScale(.65f);
input->setDelegate(node);
return input;
}
template<class C, class T>
Slider* createSlider(C* node, GeodeSettingValue<T>* setting, float width) {
auto slider = Slider::create(
node, menu_selector(C::onSlider), .5f
);
slider->setPosition(-50.f, -15.f);
node->updateSlider();
return slider;
}
template<class C, class T>
std::pair<
CCMenuItemSpriteExtra*, CCMenuItemSpriteExtra*
> createArrows(C* node, GeodeSettingValue<T>* setting, float width, bool big) {
auto yPos = setting->castDefinition().controls.slider ? 5.f : 0.f;
auto decArrowSpr = CCSprite::createWithSpriteFrameName(
big ? "double-nav.png"_spr : "navArrowBtn_001.png"
);
decArrowSpr->setFlipX(true);
decArrowSpr->setScale(.3f);
auto decArrow = CCMenuItemSpriteExtra::create(
decArrowSpr, node, menu_selector(C::onArrow)
);
decArrow->setPosition(-width / 2 + (big ? 65.f : 80.f), yPos);
decArrow->setTag(big ?
-setting->castDefinition().controls.bigArrowStep :
-setting->castDefinition().controls.arrowStep
);
auto incArrowSpr = CCSprite::createWithSpriteFrameName(
big ? "double-nav.png"_spr : "navArrowBtn_001.png"
);
incArrowSpr->setScale(.3f);
auto incArrow = CCMenuItemSpriteExtra::create(
incArrowSpr, node, menu_selector(C::onArrow)
);
incArrow->setTag(big ?
setting->castDefinition().controls.bigArrowStep :
setting->castDefinition().controls.arrowStep
);
incArrow->setPosition(big ? 5.f : -10.f, yPos);
return { decArrow, incArrow };
}
// BoolSettingNode
void BoolSettingNode::valueChanged(bool updateText) {
GeodeSettingNode::valueChanged(updateText);
m_toggle->toggle(m_uncommittedValue);
}
void BoolSettingNode::onToggle(CCObject*) {
m_uncommittedValue = !m_toggle->isToggled();
this->valueChanged(true);
m_toggle->toggle(!m_uncommittedValue);
}
bool BoolSettingNode::setup(BoolSettingValue* setting, float width) {
m_toggle = CCMenuItemToggler::createWithStandardSprites(
this, menu_selector(BoolSettingNode::onToggle), .6f
);
m_toggle->toggle(m_uncommittedValue);
m_toggle->setPositionX(-10.f);
m_menu->addChild(m_toggle);
return true;
}
// IntSettingNode
float IntSettingNode::setupHeight(IntSettingValue* setting) const {
return setting->castDefinition().controls.slider ? 55.f : 40.f;
}
void IntSettingNode::onSlider(CCObject* slider) {
m_uncommittedValue = valueFromSlider(
setting()->castDefinition(),
static_cast<SliderThumb*>(slider)->getValue()
);
this->valueChanged(true);
}
void IntSettingNode::valueChanged(bool updateText) {
GeodeSettingNode::valueChanged(updateText);
if (updateText) {
this->updateLabel();
}
this->updateSlider();
}
void IntSettingNode::updateSlider() {
if (m_slider) {
m_slider->setValue(valueToSlider(
setting()->castDefinition(),
m_uncommittedValue
));
}
}
void IntSettingNode::updateLabel() {
if (m_input) {
// hacky way to make setString not called textChanged
m_input->setString(numToString(m_uncommittedValue), false);
}
else {
m_label->setString(numToString(m_uncommittedValue).c_str());
m_label->limitLabelWidth(m_width / 2 - 70.f, .5f, .1f);
}
}
void IntSettingNode::onArrow(CCObject* sender) {
m_uncommittedValue += sender->getTag();
this->valueChanged(true);
}
void IntSettingNode::textChanged(CCTextInputNode* input) {
auto res = numFromString<ValueType>(input->getString());
if (!res) return;
m_uncommittedValue = res.unwrap();
this->valueChanged(false);
}
bool IntSettingNode::setup(IntSettingValue* setting, float width) {
if (setting->castDefinition().controls.input) {
m_menu->addChild(m_input = createInput(this, setting, width));
m_input->setCommonFilter(CommonFilter::Int);
}
else {
m_label = CCLabelBMFont::create("", "bigFont.fnt");
m_label->setPosition({
-(width / 2 - 70.f) / 2,
setting->castDefinition().controls.slider ? 5.f : 0.f
});
m_label->limitLabelWidth(width / 2 - 70.f, .5f, .1f);
m_menu->addChild(m_label);
}
if (setting->castDefinition().controls.slider) {
m_menu->addChild(m_slider = createSlider(this, setting, width));
}
if (setting->castDefinition().controls.arrows) {
auto [dec, inc] = createArrows(this, setting, width, false);
m_menu->addChild(m_decArrow = dec);
m_menu->addChild(m_incArrow = inc);
}
if (setting->castDefinition().controls.bigArrows) {
auto [dec, inc] = createArrows(this, setting, width, true);
m_menu->addChild(m_bigDecArrow = dec);
m_menu->addChild(m_bigIncArrow = inc);
}
return true;
}
// FloatSettingNode
float FloatSettingNode::setupHeight(FloatSettingValue* setting) const {
return setting->castDefinition().controls.slider ? 55.f : 40.f;
}
void FloatSettingNode::onSlider(CCObject* slider) {
m_uncommittedValue = valueFromSlider(
setting()->castDefinition(),
static_cast<SliderThumb*>(slider)->getValue()
);
this->valueChanged(true);
}
void FloatSettingNode::valueChanged(bool updateText) {
GeodeSettingNode::valueChanged(updateText);
if (updateText) {
this->updateLabel();
}
this->updateSlider();
}
void FloatSettingNode::updateSlider() {
if (m_slider) {
m_slider->setValue(valueToSlider(
setting()->castDefinition(),
m_uncommittedValue
));
}
}
void FloatSettingNode::updateLabel() {
if (m_input) {
// hacky way to make setString not called textChanged
m_input->setString(numToString(m_uncommittedValue), false);
}
else {
m_label->setString(numToString(m_uncommittedValue).c_str());
m_label->limitLabelWidth(m_width / 2 - 70.f, .5f, .1f);
}
}
void FloatSettingNode::onArrow(CCObject* sender) {
m_uncommittedValue += sender->getTag();
this->valueChanged(true);
}
void FloatSettingNode::textChanged(CCTextInputNode* input) {
auto res = numFromString<ValueType>(input->getString());
if (!res) return;
m_uncommittedValue = res.unwrap();
this->valueChanged(false);
}
bool FloatSettingNode::setup(FloatSettingValue* setting, float width) {
if (setting->castDefinition().controls.input) {
m_menu->addChild(m_input = createInput(this, setting, width));
m_input->setCommonFilter(CommonFilter::Float);
}
else {
m_label = CCLabelBMFont::create("", "bigFont.fnt");
m_label->setPosition({
-(width / 2 - 70.f) / 2,
setting->castDefinition().controls.slider ? 5.f : 0.f
});
m_label->limitLabelWidth(width / 2 - 70.f, .5f, .1f);
m_menu->addChild(m_label);
}
if (setting->castDefinition().controls.slider) {
m_menu->addChild(m_slider = createSlider(this, setting, width));
}
if (setting->castDefinition().controls.arrows) {
auto [dec, inc] = createArrows(this, setting, width, false);
m_menu->addChild(m_decArrow = dec);
m_menu->addChild(m_incArrow = inc);
}
if (setting->castDefinition().controls.bigArrows) {
auto [dec, inc] = createArrows(this, setting, width, true);
m_menu->addChild(m_bigDecArrow = dec);
m_menu->addChild(m_bigIncArrow = inc);
}
return true;
}
// StringSettingNode
void StringSettingNode::updateLabel() {
if (m_input) m_input->setString(m_uncommittedValue, false);
if (m_label) {
m_label->setString(m_uncommittedValue.c_str());
m_label->limitLabelWidth(m_width / 2 - 80.f, .65f, .1f);
}
}
void StringSettingNode::textChanged(CCTextInputNode* input) {
m_uncommittedValue = input->getString();
this->valueChanged(false);
}
void StringSettingNode::valueChanged(bool updateText) {
GeodeSettingNode::valueChanged(updateText);
this->updateLabel();
}
void StringSettingNode::onArrow(CCObject* sender) {
m_selectedOption += sender->getTag();
if (m_selectedOption < 0) {
m_selectedOption = m_options.size() - 1;
}
else if (m_selectedOption >= m_options.size()) {
m_selectedOption = 0;
}
m_uncommittedValue = m_options[m_selectedOption];
this->valueChanged(true);
}
bool StringSettingNode::setup(StringSettingValue* setting, float width) {
m_width = width;
if (setting->castDefinition().controls->options.has_value()) {
m_options = setting->castDefinition().controls->options.value();
m_selectedOption = 0;
for (size_t i = 0; i < m_options.size(); i++) {
if (m_options[i] == setting->castDefinition().defaultValue) {
m_selectedOption = i;
break;
}
}
auto prevArrowSpr = CCSprite::createWithSpriteFrameName("navArrowBtn_001.png");
prevArrowSpr->setFlipX(true);
prevArrowSpr->setScale(.3f);
m_prevBtn = CCMenuItemSpriteExtra::create(
prevArrowSpr, this, menu_selector(StringSettingNode::onArrow)
);
m_prevBtn->setPosition(-width / 2 + 65.f, 0.f);
m_prevBtn->setTag(-1);
m_menu->addChild(m_prevBtn);
auto nextArrowSpr = CCSprite::createWithSpriteFrameName("navArrowBtn_001.png");
nextArrowSpr->setScale(.3f);
m_nextBtn = CCMenuItemSpriteExtra::create(
nextArrowSpr, this, menu_selector(StringSettingNode::onArrow)
);
m_nextBtn->setTag(1);
m_nextBtn->setPosition(5.f, 0.f);
m_menu->addChild(m_nextBtn);
m_label = CCLabelBMFont::create("", "bigFont.fnt");
m_label->setPosition({ -(width / 2 - 70.f) / 2, .0f });
m_menu->addChild(m_label);
}
else {
m_input = TextInput::create(width / 2 - 10.f, "Text", "chatFont.fnt");
m_input->setPosition({ -(width / 2 - 70.f) / 2, .0f });
m_input->setScale(.65f);
if (setting->castDefinition().controls->filter.has_value()) {
m_input->setFilter(setting->castDefinition().controls->filter.value());
}
m_input->setDelegate(this);
m_menu->addChild(m_input);
}
return true;
}
// FileSettingNode
void FileSettingNode::updateLabel() {
// hacky way to make setString not called textChanged
m_input->setString(m_uncommittedValue.string(), false);
}
void FileSettingNode::textChanged(CCTextInputNode* input) {
m_uncommittedValue = input->getString().c_str();
this->valueChanged(false);
}
void FileSettingNode::valueChanged(bool updateText) {
GeodeSettingNode::valueChanged(updateText);
this->updateLabel();
}
void FileSettingNode::onPickFile(CCObject*) {
m_pickListener.bind(this, &FileSettingNode::onPickFileFinished);
m_pickListener.setFilter(file::pick(
file::PickMode::OpenFile,
{
dirs::getGameDir(),
setting()->castDefinition().controls.filters
}));
}
void FileSettingNode::onPickFileFinished(FileTask::Event* event) {
if (!event->getValue()) {
return;
}
auto value = event->getValue();
if (value->isOk()) {
m_uncommittedValue = value->unwrap();
this->valueChanged(true);
} else {
FLAlertLayer::create(
"Failed",
fmt::format("Failed to pick file: {}", value->err().value()).c_str(),
"Ok"
)->show();
}
}
bool FileSettingNode::setup(FileSettingValue* setting, float width) {
m_input = TextInput::create(width / 2 - 30.f, "Path to File", "chatFont.fnt");
m_input->setPosition({ -(width / 2 - 80.f) / 2 - 15.f, .0f });
m_input->setScale(.65f);
m_input->setDelegate(this);
m_menu->addChild(m_input);
auto fileBtnSpr = CCSprite::createWithSpriteFrameName("gj_folderBtn_001.png");
fileBtnSpr->setScale(.5f);
auto fileBtn = CCMenuItemSpriteExtra::create(
fileBtnSpr, this, menu_selector(FileSettingNode::onPickFile)
);
fileBtn->setPosition(.0f, .0f);
m_menu->addChild(fileBtn);
return true;
}
// ColorSettingNode
void ColorSettingNode::valueChanged(bool updateText) {
GeodeSettingNode::valueChanged(updateText);
m_colorSpr->setColor(m_uncommittedValue);
}
void ColorSettingNode::updateColor(ccColor4B const& color) {
m_uncommittedValue = to3B(color);
this->valueChanged(true);
}
void ColorSettingNode::onSelectColor(CCObject*) {
auto popup = ColorPickPopup::create(m_uncommittedValue);
popup->setDelegate(this);
popup->setColorTarget(m_colorSpr);
popup->show();
}
bool ColorSettingNode::setup(ColorSettingValue* setting, float width) {
m_colorSpr = ColorChannelSprite::create();
m_colorSpr->setColor(m_uncommittedValue);
m_colorSpr->setScale(.65f);
auto button = CCMenuItemSpriteExtra::create(
m_colorSpr, this, menu_selector(ColorSettingNode::onSelectColor)
);
button->setPositionX(-10.f);
m_menu->addChild(button);
return true;
}
// ColorAlphaSettingNode
void ColorAlphaSettingNode::valueChanged(bool updateText) {
GeodeSettingNode::valueChanged(updateText);
m_colorSpr->setColor(to3B(m_uncommittedValue));
m_colorSpr->updateOpacity(m_uncommittedValue.a / 255.f);
}
void ColorAlphaSettingNode::updateColor(ccColor4B const& color) {
m_uncommittedValue = color;
this->valueChanged(true);
}
void ColorAlphaSettingNode::onSelectColor(CCObject*) {
auto popup = ColorPickPopup::create(m_uncommittedValue);
popup->setDelegate(this);
popup->setColorTarget(m_colorSpr);
popup->show();
}
bool ColorAlphaSettingNode::setup(ColorAlphaSettingValue* setting, float width) {
m_colorSpr = ColorChannelSprite::create();
m_colorSpr->setColor(to3B(m_uncommittedValue));
m_colorSpr->updateOpacity(m_uncommittedValue.a / 255.f);
m_colorSpr->setScale(.65f);
auto button = CCMenuItemSpriteExtra::create(
m_colorSpr, this, menu_selector(ColorAlphaSettingNode::onSelectColor)
);
button->setPositionX(-10.f);
m_menu->addChild(button);
return true;
}
// CustomSettingPlaceholderNode
void CustomSettingPlaceholderNode::commit() {
}
bool CustomSettingPlaceholderNode::hasUncommittedChanges() {
return false;
}
bool CustomSettingPlaceholderNode::hasNonDefaultValue() {
return false;
}
void CustomSettingPlaceholderNode::resetToDefault() {
}
bool CustomSettingPlaceholderNode::init(std::string const& key, float width) {
if (!SettingNode::init(nullptr))
return false;
constexpr auto sidePad = 40.f;
this->setContentSize({ width, 40.f });
auto info = CCLabelBMFont::create(
"You need to enable the mod to modify this setting.",
"bigFont.fnt"
);
info->setAnchorPoint({ .0f, .5f });
info->limitLabelWidth(width - sidePad, .5f, .1f);
info->setPosition({ sidePad / 2, m_obContentSize.height / 2 });
this->addChild(info);
return true;
}
CustomSettingPlaceholderNode* CustomSettingPlaceholderNode::create(
std::string const& key, float width
) {
auto ret = new CustomSettingPlaceholderNode;
if (ret->init(key, width)) {
ret->autorelease();
return ret;
}
delete ret;
return nullptr;
}

View file

@ -1,361 +0,0 @@
#pragma once
#include <Geode/loader/Event.hpp>
#include <Geode/utils/Result.hpp>
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/CCTextInputNode.hpp>
#include <Geode/binding/Slider.hpp>
#include <Geode/binding/SliderThumb.hpp>
#include <Geode/loader/Setting.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/ui/BasedButtonSprite.hpp>
#include <Geode/ui/ColorPickPopup.hpp>
#include <Geode/ui/InputNode.hpp>
#include <Geode/ui/Popup.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/loader/Mod.hpp>
using namespace geode::prelude;
#define IMPL_SETT_CREATE(type_) \
static type_##SettingNode* create( \
type_##SettingValue* value, float width \
) { \
auto ret = new type_##SettingNode; \
if (ret->init(value, width)) { \
ret->autorelease(); \
return ret; \
} \
delete ret; \
return nullptr; \
}
template <class T>
class GeodeSettingNode : public SettingNode {
public:
using ValueType = typename T::ValueType;
protected:
float m_width;
float m_height;
ValueType m_uncommittedValue;
CCMenu* m_menu;
CCLabelBMFont* m_nameLabel;
CCLabelBMFont* m_errorLabel;
CCMenuItemSpriteExtra* m_resetBtn;
bool init(GeodeSettingValue<T>* setting, float width) {
if (!SettingNode::init(setting))
return false;
m_width = width;
m_height = this->setupHeight(setting);
this->setContentSize({ width, m_height });
constexpr auto sidePad = 40.f;
m_uncommittedValue = setting->getValue();
auto name = setting->getDefinition().getDisplayName();
m_nameLabel = CCLabelBMFont::create(name.c_str(), "bigFont.fnt");
m_nameLabel->setAnchorPoint({ .0f, .5f });
m_nameLabel->limitLabelWidth(width / 2 - 50.f, .5f, .1f);
m_nameLabel->setPosition({ sidePad / 2, m_obContentSize.height / 2 });
this->addChild(m_nameLabel);
m_errorLabel = CCLabelBMFont::create("", "bigFont.fnt");
m_errorLabel->setAnchorPoint({ .0f, .5f });
m_errorLabel->limitLabelWidth(width / 2 - 50.f, .25f, .1f);
m_errorLabel->setPosition({ sidePad / 2, m_obContentSize.height / 2 - 14.f });
m_errorLabel->setColor({ 255, 100, 100 });
m_errorLabel->setZOrder(5);
this->addChild(m_errorLabel);
m_menu = CCMenu::create();
m_menu->setPosition({ m_obContentSize.width - sidePad / 2, m_obContentSize.height / 2 }
);
this->addChild(m_menu);
float btnPos = 15.f;
if (setting->castDefinition().description) {
auto infoSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png");
infoSpr->setScale(.6f);
auto infoBtn = CCMenuItemSpriteExtra::create(
infoSpr, this, menu_selector(GeodeSettingNode::onDescription)
);
infoBtn->setPosition(
-m_obContentSize.width + sidePad + m_nameLabel->getScaledContentSize().width +
btnPos,
0.f
);
m_menu->addChild(infoBtn);
btnPos += 20.f;
}
auto resetBtnSpr = CCSprite::createWithSpriteFrameName("reset-gold.png"_spr);
resetBtnSpr->setScale(.5f);
m_resetBtn = CCMenuItemSpriteExtra::create(
resetBtnSpr, this, menu_selector(GeodeSettingNode::onReset)
);
m_resetBtn->setPosition(
-m_obContentSize.width + sidePad + m_nameLabel->getScaledContentSize().width +
btnPos,
.0f
);
m_menu->addChild(m_resetBtn);
m_menu->setTouchEnabled(true);
if (!this->setup(setting, width)) return false;
this->valueChanged();
return true;
}
void onDescription(CCObject*) {
FLAlertLayer::create(
setting()->getDefinition().getDisplayName().c_str(),
setting()->castDefinition().description.value(),
"OK"
)->show();
}
void onReset(CCObject*) {
createQuickPopup(
"Reset",
"Are you sure you want to <cr>reset</c> <cl>" +
setting()->getDefinition().getDisplayName() +
"</c> to <cy>default</c>?",
"Cancel", "Reset",
[this](auto, bool btn2) {
if (btn2) {
this->resetToDefault();
}
}
);
}
virtual float setupHeight(GeodeSettingValue<T>* setting) const {
return 40.f;
}
virtual bool setup(GeodeSettingValue<T>* setting, float width) = 0;
virtual void valueChanged(bool updateText = true) {
if (this->hasUncommittedChanges()) {
m_nameLabel->setColor({0x11, 0xdd, 0x00});
}
else {
m_nameLabel->setColor({0xff, 0xff, 0xff});
}
if (m_resetBtn) m_resetBtn->setVisible(this->hasNonDefaultValue());
auto isValid = setting()->validate(m_uncommittedValue);
if (!isValid) {
m_errorLabel->setVisible(true);
m_errorLabel->setString(isValid.unwrapErr().c_str());
}
else {
m_errorLabel->setVisible(false);
}
this->dispatchChanged();
}
GeodeSettingValue<T>* setting() {
return static_cast<GeodeSettingValue<T>*>(m_value);
}
public:
void commit() override {
setting()->setValue(m_uncommittedValue);
m_uncommittedValue = setting()->getValue();
this->valueChanged();
this->dispatchCommitted();
}
bool hasUncommittedChanges() override {
return m_uncommittedValue != setting()->getValue();
}
bool hasNonDefaultValue() override {
return m_uncommittedValue != setting()->castDefinition().defaultValue;
}
void resetToDefault() override {
m_uncommittedValue = setting()->castDefinition().defaultValue;
this->valueChanged();
}
};
class BoolSettingNode : public GeodeSettingNode<BoolSetting> {
protected:
CCMenuItemToggler* m_toggle;
void onToggle(CCObject*);
void valueChanged(bool updateText) override;
bool setup(BoolSettingValue* setting, float width) override;
public:
IMPL_SETT_CREATE(Bool);
};
class IntSettingNode :
public GeodeSettingNode<IntSetting>,
public TextInputDelegate
{
protected:
TextInput* m_input = nullptr;
CCLabelBMFont* m_label = nullptr;
Slider* m_slider = nullptr;
CCMenuItemSpriteExtra* m_decArrow = nullptr;
CCMenuItemSpriteExtra* m_incArrow = nullptr;
CCMenuItemSpriteExtra* m_bigDecArrow = nullptr;
CCMenuItemSpriteExtra* m_bigIncArrow = nullptr;
void valueChanged(bool updateText) override;
void textChanged(CCTextInputNode* input) override;
float setupHeight(IntSettingValue* setting) const override;
bool setup(IntSettingValue* setting, float width) override;
public:
void updateSlider();
void updateLabel();
void onSlider(CCObject* slider);
void onArrow(CCObject* sender);
IMPL_SETT_CREATE(Int);
};
class FloatSettingNode :
public GeodeSettingNode<FloatSetting>,
public TextInputDelegate
{
protected:
TextInput* m_input = nullptr;
CCLabelBMFont* m_label = nullptr;
Slider* m_slider = nullptr;
CCMenuItemSpriteExtra* m_decArrow = nullptr;
CCMenuItemSpriteExtra* m_incArrow = nullptr;
CCMenuItemSpriteExtra* m_bigDecArrow = nullptr;
CCMenuItemSpriteExtra* m_bigIncArrow = nullptr;
void valueChanged(bool updateText) override;
void textChanged(CCTextInputNode* input) override;
float setupHeight(FloatSettingValue* setting) const override;
bool setup(FloatSettingValue* setting, float width) override;
public:
void updateSlider();
void updateLabel();
void onSlider(CCObject* slider);
void onArrow(CCObject* sender);
IMPL_SETT_CREATE(Float);
};
class StringSettingNode :
public GeodeSettingNode<StringSetting>,
public TextInputDelegate
{
protected:
TextInput* m_input = nullptr;
CCLabelBMFont* m_label = nullptr;
CCMenuItemSpriteExtra* m_prevBtn = nullptr;
CCMenuItemSpriteExtra* m_nextBtn = nullptr;
std::vector<std::string> m_options;
int m_selectedOption = 0;
float m_width;
void textChanged(CCTextInputNode* input) override;
void valueChanged(bool updateText) override;
void updateLabel();
void onArrow(CCObject* sender);
bool setup(StringSettingValue* setting, float width) override;
public:
IMPL_SETT_CREATE(String);
};
class FileSettingNode :
public GeodeSettingNode<FileSetting>,
public TextInputDelegate
{
protected:
using FileTask = Task<Result<std::filesystem::path>>;
TextInput* m_input;
EventListener<FileTask> m_pickListener;
void textChanged(CCTextInputNode* input) override;
void valueChanged(bool updateText) override;
void updateLabel();
void onPickFile(CCObject*);
void onPickFileFinished(FileTask::Event* event);
bool setup(FileSettingValue* setting, float width) override;
public:
IMPL_SETT_CREATE(File);
};
class ColorSettingNode :
public GeodeSettingNode<ColorSetting>,
public ColorPickPopupDelegate {
protected:
ColorChannelSprite* m_colorSpr;
void valueChanged(bool updateText) override;
void updateColor(ccColor4B const& color) override;
void onSelectColor(CCObject*);
bool setup(ColorSettingValue* setting, float width) override;
public:
IMPL_SETT_CREATE(Color);
};
class ColorAlphaSettingNode :
public GeodeSettingNode<ColorAlphaSetting>,
public ColorPickPopupDelegate {
protected:
ColorChannelSprite* m_colorSpr;
void valueChanged(bool updateText) override;
void updateColor(ccColor4B const& color) override;
void onSelectColor(CCObject*);
bool setup(ColorAlphaSettingValue* setting, float width) override;
public:
IMPL_SETT_CREATE(ColorAlpha);
};
class CustomSettingPlaceholderNode : public SettingNode {
protected:
void commit() override;
bool hasUncommittedChanges() override;
bool hasNonDefaultValue() override;
void resetToDefault() override;
bool init(std::string const& key, float width);
public:
static CustomSettingPlaceholderNode* create(
std::string const& key, float width
);
};

View file

@ -1,7 +1,6 @@
#include "ModSettingsPopup.hpp"
#include <Geode/binding/ButtonSprite.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/Setting.hpp>
#include <Geode/loader/ModSettingsManager.hpp>
#include <Geode/ui/ScrollLayer.hpp>
#include <Geode/utils/cocos.hpp>

View file

@ -1,105 +0,0 @@
#include <Geode/binding/CCTextInputNode.hpp>
#include <Geode/binding/TextInputDelegate.hpp>
#include <Geode/ui/InputNode.hpp>
#include <Geode/ui/TextInput.hpp>
#include <Geode/utils/cocos.hpp>
using namespace geode::prelude;
std::string InputNode::getString() {
return m_input->getString();
}
void InputNode::setString(std::string const& str) {
m_input->setString(str);
}
CCTextInputNode* InputNode::getInput() const {
return m_input;
}
CCScale9Sprite* InputNode::getBG() const {
return m_bgSprite;
}
void InputNode::setEnabled(bool enabled) {
m_input->setMouseEnabled(enabled);
m_input->setTouchEnabled(enabled);
}
bool InputNode::init(
float width, float height, char const* placeholder, char const* font, std::string const& filter,
int maxCharCount
) {
if (!CCMenuItem::initWithTarget(this, nullptr)) return false;
m_bgSprite = cocos2d::extension::CCScale9Sprite::create(
"square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f }
);
m_bgSprite->setScale(.5f);
m_bgSprite->setColor({ 0, 0, 0 });
m_bgSprite->setOpacity(75);
m_bgSprite->setContentSize({ width * 2, height * 2 });
m_bgSprite->setPosition(width / 2, height / 2);
this->addChild(m_bgSprite);
m_input = CCTextInputNode::create(width - 10.0f, height, placeholder, "Thonburi", 24, font);
m_input->setLabelPlaceholderColor({ 150, 150, 150 });
m_input->setLabelPlaceholderScale(.75f);
m_input->setMaxLabelScale(.85f);
m_input->setMaxLabelLength(maxCharCount);
m_input->setPosition(width / 2, height / 2);
m_input->setUserObject("fix-text-input", CCBool::create(true));
if (filter.length()) {
m_input->setAllowedChars(filter);
}
this->addChild(m_input);
this->setContentSize({ width, height });
this->setAnchorPoint({ .5f, .5f });
this->setCascadeColorEnabled(true);
this->setCascadeOpacityEnabled(true);
return true;
}
bool InputNode::init(
float width, char const* placeholder, char const* font, std::string const& filter,
int maxCharCount
) {
return init(width, 30.0f, placeholder, font, filter, maxCharCount);
}
InputNode* InputNode::create(
float width, char const* placeholder, char const* font, std::string const& filter,
int maxCharCount
) {
auto pRet = new InputNode();
if (pRet->init(width, placeholder, font, filter, maxCharCount)) {
pRet->autorelease();
return pRet;
}
delete pRet;
return nullptr;
}
InputNode* InputNode::create(float width, char const* placeholder, std::string const& filter) {
return create(width, placeholder, "bigFont.fnt", filter, 69);
}
InputNode* InputNode::create(
float width, char const* placeholder, std::string const& filter, int maxCharCount
) {
return create(width, placeholder, "bigFont.fnt", filter, maxCharCount);
}
InputNode* InputNode::create(float width, char const* placeholder, char const* font) {
return create(width, placeholder, font, "", 69);
}
InputNode* InputNode::create(float width, char const* placeholder) {
return create(width, placeholder, "bigFont.fnt");
}

View file

@ -317,21 +317,9 @@ struct MDParser {
auto split = splitOnce(s_lastImage, '?');
s_lastImage = split.first;
// TODO: remove this in v4.0.0
// check if this image is using the old format "my.mod/image.png&scale:0.5"
// this will be deprecated and then removed in the future
if (utils::string::contains(s_lastImage, "&")) {
split = splitOnce(s_lastImage, '&');
s_lastImage = split.first;
imgArguments = ranges::map<decltype(imgArguments)>(utils::string::split(split.second, "&"), [&](auto str) {
return splitOnce(str, ':');
});
} else {
// new format "my.mod/image.png?scale=0.5"
imgArguments = ranges::map<decltype(imgArguments)>(utils::string::split(split.second, "&"), [&](auto str) {
return splitOnce(str, '=');
});
}
imgArguments = ranges::map<decltype(imgArguments)>(utils::string::split(split.second, "&"), [&](auto str) {
return splitOnce(str, '=');
});
float spriteScale = 1.0f;
float spriteWidth = 0.0f;

View file

@ -1,6 +1,5 @@
#include <Geode/Loader.hpp>
#include <Geode/modify/MenuLayer.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/loader/Dispatch.hpp>
#include <Geode/Bindings.hpp>
#include "main.hpp"
@ -30,119 +29,120 @@ constexpr Icon DEFAULT_ICON = Icon::Steve;
class MySettingValue;
class MySettingValue : public SettingValue {
protected:
Icon m_icon;
// TODO: v4 (port to new settings)
// class MySettingValue : public SettingValue {
// protected:
// Icon m_icon;
public:
MySettingValue(std::string const& key, std::string const& modID, Icon icon)
: SettingValue(key, modID), m_icon(icon) {}
// public:
// MySettingValue(std::string const& key, std::string const& modID, Icon icon)
// : SettingValue(key, modID), m_icon(icon) {}
bool load(matjson::Value const& json) override {
if (!json.is<int>()) return false;
m_icon = static_cast<Icon>(json.as<int>());
return true;
}
bool save(matjson::Value& json) const override {
json = static_cast<int>(m_icon);
return true;
}
SettingNode* createNode(float width) override;
// bool load(matjson::Value const& json) override {
// if (!json.is<int>()) return false;
// m_icon = static_cast<Icon>(json.as<int>());
// return true;
// }
// bool save(matjson::Value& json) const override {
// json = static_cast<int>(m_icon);
// return true;
// }
// SettingNode* createNode(float width) override;
void setIcon(Icon icon) {
m_icon = icon;
}
Icon getIcon() const {
return m_icon;
}
};
// void setIcon(Icon icon) {
// m_icon = icon;
// }
// Icon getIcon() const {
// return m_icon;
// }
// };
class MySettingNode : public SettingNode {
protected:
Icon m_currentIcon;
std::vector<CCSprite*> m_sprites;
// class MySettingNode : public SettingNode {
// protected:
// Icon m_currentIcon;
// std::vector<CCSprite*> m_sprites;
bool init(MySettingValue* value, float width) {
if (!SettingNode::init(value))
return false;
// bool init(MySettingValue* value, float width) {
// if (!SettingNode::init(value))
// return false;
m_currentIcon = value->getIcon();
this->setContentSize({ width, 40.f });
// m_currentIcon = value->getIcon();
// this->setContentSize({ width, 40.f });
auto menu = CCMenu::create();
menu->setPosition(width / 2, 20.f);
// auto menu = CCMenu::create();
// menu->setPosition(width / 2, 20.f);
float x = -75.f;
// float x = -75.f;
for (auto& [spr, icon] : {
std::pair { "GJ_square01.png", Icon::Steve, },
std::pair { "GJ_square02.png", Icon::Mike, },
std::pair { "GJ_square03.png", Icon::LazarithTheDestroyerOfForsakenSouls, },
std::pair { "GJ_square04.png", Icon::Geoff, },
}) {
auto btnSpr = CCSprite::create(spr);
btnSpr->setScale(.7f);
m_sprites.push_back(btnSpr);
if (icon == m_currentIcon) {
btnSpr->setColor({ 0, 255, 0 });
} else {
btnSpr->setColor({ 200, 200, 200 });
}
auto btn = CCMenuItemSpriteExtra::create(
btnSpr, this, menu_selector(MySettingNode::onSelect)
);
btn->setTag(static_cast<int>(icon));
btn->setPosition(x, 0);
menu->addChild(btn);
// for (auto& [spr, icon] : {
// std::pair { "GJ_square01.png", Icon::Steve, },
// std::pair { "GJ_square02.png", Icon::Mike, },
// std::pair { "GJ_square03.png", Icon::LazarithTheDestroyerOfForsakenSouls, },
// std::pair { "GJ_square04.png", Icon::Geoff, },
// }) {
// auto btnSpr = CCSprite::create(spr);
// btnSpr->setScale(.7f);
// m_sprites.push_back(btnSpr);
// if (icon == m_currentIcon) {
// btnSpr->setColor({ 0, 255, 0 });
// } else {
// btnSpr->setColor({ 200, 200, 200 });
// }
// auto btn = CCMenuItemSpriteExtra::create(
// btnSpr, this, menu_selector(MySettingNode::onSelect)
// );
// btn->setTag(static_cast<int>(icon));
// btn->setPosition(x, 0);
// menu->addChild(btn);
x += 50.f;
}
// x += 50.f;
// }
this->addChild(menu);
// this->addChild(menu);
return true;
}
// return true;
// }
void onSelect(CCObject* sender) {
for (auto& spr : m_sprites) {
spr->setColor({ 200, 200, 200 });
}
m_currentIcon = static_cast<Icon>(sender->getTag());
static_cast<CCSprite*>(
static_cast<CCMenuItemSpriteExtra*>(sender)->getNormalImage()
)->setColor({ 0, 255, 0 });
this->dispatchChanged();
}
// void onSelect(CCObject* sender) {
// for (auto& spr : m_sprites) {
// spr->setColor({ 200, 200, 200 });
// }
// m_currentIcon = static_cast<Icon>(sender->getTag());
// static_cast<CCSprite*>(
// static_cast<CCMenuItemSpriteExtra*>(sender)->getNormalImage()
// )->setColor({ 0, 255, 0 });
// this->dispatchChanged();
// }
public:
void commit() override {
static_cast<MySettingValue*>(m_value)->setIcon(m_currentIcon);
this->dispatchCommitted();
}
bool hasUncommittedChanges() override {
return m_currentIcon != static_cast<MySettingValue*>(m_value)->getIcon();
}
bool hasNonDefaultValue() override {
return m_currentIcon != DEFAULT_ICON;
}
void resetToDefault() override {
m_currentIcon = DEFAULT_ICON;
}
// public:
// void commit() override {
// static_cast<MySettingValue*>(m_value)->setIcon(m_currentIcon);
// this->dispatchCommitted();
// }
// bool hasUncommittedChanges() override {
// return m_currentIcon != static_cast<MySettingValue*>(m_value)->getIcon();
// }
// bool hasNonDefaultValue() override {
// return m_currentIcon != DEFAULT_ICON;
// }
// void resetToDefault() override {
// m_currentIcon = DEFAULT_ICON;
// }
static MySettingNode* create(MySettingValue* value, float width) {
auto ret = new MySettingNode;
if (ret->init(value, width)) {
ret->autorelease();
return ret;
}
delete ret;
return nullptr;
}
};
// static MySettingNode* create(MySettingValue* value, float width) {
// auto ret = new MySettingNode;
// if (ret->init(value, width)) {
// ret->autorelease();
// return ret;
// }
// delete ret;
// return nullptr;
// }
// };
SettingNode* MySettingValue::createNode(float width) {
return MySettingNode::create(this, width);
}
// SettingNode* MySettingValue::createNode(float width) {
// return MySettingNode::create(this, width);
// }
struct MyMenuLayer : Modify<MyMenuLayer, MenuLayer> {
void onMoreGames(CCObject*) {
@ -161,7 +161,7 @@ struct MyMenuLayer : Modify<MyMenuLayer, MenuLayer> {
};
$on_mod(Loaded) {
Mod::get()->addCustomSetting<MySettingValue>("overcast-skies", DEFAULT_ICON);
// Mod::get()->addCustomSetting<MySettingValue>("overcast-skies", DEFAULT_ICON);
(void)new EventListener(+[](GJGarageLayer* gl) {
auto label = CCLabelBMFont::create("Dispatcher works!", "bigFont.fnt");