Add geode
This commit is contained in:
1 parent
ceba3130ab
commit
9c61b0a7fd
421 files changed
+30990
No files matched your search
@@ -0,0 +1,384 @@
|
||||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Event.hpp>
|
||||
#include <cocos2d.h>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
#ifdef HJFOD_CUSTOM_KEYBINDS_EXPORTING
|
||||
#define CUSTOM_KEYBINDS_DLL __declspec(dllexport)
|
||||
#else
|
||||
#define CUSTOM_KEYBINDS_DLL __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define CUSTOM_KEYBINDS_DLL __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
struct BindSaveData;
|
||||
|
||||
namespace keybinds {
|
||||
class Bind;
|
||||
class BindManager;
|
||||
|
||||
using DeviceID = std::string;
|
||||
|
||||
/**
|
||||
* Base class for implementing bindings for different input devices
|
||||
*/
|
||||
class CUSTOM_KEYBINDS_DLL Bind : public cocos2d::CCObject {
|
||||
protected:
|
||||
friend class BindManager;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get the hash for this bind
|
||||
*/
|
||||
virtual size_t getHash() const = 0;
|
||||
/**
|
||||
* Check if this bind is equal to another. By default compares hashes
|
||||
*/
|
||||
virtual bool isEqual(Bind* other) const;
|
||||
/**
|
||||
* Get the bind's representation as a human-readable string
|
||||
*/
|
||||
virtual std::string toString() const = 0;
|
||||
virtual cocos2d::CCNode* createLabel() const;
|
||||
virtual DeviceID getDeviceID() const = 0;
|
||||
virtual matjson::Value save() const = 0;
|
||||
|
||||
virtual ~Bind() = default;
|
||||
|
||||
cocos2d::CCNodeRGBA* createBindSprite() const;
|
||||
};
|
||||
|
||||
enum class Modifier : unsigned int {
|
||||
None = 0b0000,
|
||||
Control = 0b0001,
|
||||
Shift = 0b0010,
|
||||
Alt = 0b0100,
|
||||
Command = 0b1000,
|
||||
#ifdef GEODE_IS_MACOS
|
||||
PlatformControl = Command,
|
||||
#else
|
||||
PlatformControl = Control,
|
||||
#endif
|
||||
};
|
||||
CUSTOM_KEYBINDS_DLL Modifier operator|(Modifier const& a, Modifier const& b);
|
||||
CUSTOM_KEYBINDS_DLL Modifier operator|=(Modifier& a, Modifier const& b);
|
||||
CUSTOM_KEYBINDS_DLL bool operator&(Modifier const& a, Modifier const& b);
|
||||
|
||||
CUSTOM_KEYBINDS_DLL std::string keyToString(cocos2d::enumKeyCodes key);
|
||||
CUSTOM_KEYBINDS_DLL bool keyIsModifier(cocos2d::enumKeyCodes key);
|
||||
CUSTOM_KEYBINDS_DLL bool keyIsController(cocos2d::enumKeyCodes key);
|
||||
|
||||
enum class MouseButton {
|
||||
Button4 = 0,
|
||||
Button5 = 1,
|
||||
};
|
||||
|
||||
CUSTOM_KEYBINDS_DLL std::string mouseToString(MouseButton button);
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL Keybind final : public Bind {
|
||||
protected:
|
||||
cocos2d::enumKeyCodes m_key;
|
||||
Modifier m_modifiers;
|
||||
|
||||
public:
|
||||
static Keybind* create(cocos2d::enumKeyCodes key, Modifier modifiers = Modifier::None);
|
||||
static Keybind* parse(matjson::Value const&);
|
||||
|
||||
cocos2d::enumKeyCodes getKey() const;
|
||||
Modifier getModifiers() const;
|
||||
|
||||
size_t getHash() const override;
|
||||
bool isEqual(Bind* other) const override;
|
||||
std::string toString() const override;
|
||||
DeviceID getDeviceID() const override;
|
||||
matjson::Value save() const override;
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL MouseBind final : public Bind {
|
||||
protected:
|
||||
MouseButton m_button;
|
||||
Modifier m_modifiers;
|
||||
|
||||
public:
|
||||
static MouseBind* create(MouseButton button, Modifier modifiers = Modifier::None);
|
||||
static MouseBind* parse(matjson::Value const&);
|
||||
|
||||
MouseButton getButton() const;
|
||||
Modifier getModifiers() const;
|
||||
|
||||
size_t getHash() const override;
|
||||
bool isEqual(Bind* other) const override;
|
||||
std::string toString() const override;
|
||||
DeviceID getDeviceID() const override;
|
||||
matjson::Value save() const override;
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL ControllerBind final : public Bind {
|
||||
protected:
|
||||
cocos2d::enumKeyCodes m_button;
|
||||
|
||||
public:
|
||||
static ControllerBind* create(cocos2d::enumKeyCodes button);
|
||||
static ControllerBind* parse(matjson::Value const&);
|
||||
|
||||
cocos2d::enumKeyCodes getButton() const;
|
||||
|
||||
size_t getHash() const override;
|
||||
bool isEqual(Bind* other) const override;
|
||||
std::string toString() const override;
|
||||
cocos2d::CCNode* createLabel() const override;
|
||||
DeviceID getDeviceID() const override;
|
||||
matjson::Value save() const override;
|
||||
};
|
||||
|
||||
struct CUSTOM_KEYBINDS_DLL BindHash {
|
||||
geode::Ref<Bind> bind;
|
||||
BindHash(Bind* bind);
|
||||
bool operator==(BindHash const& other) const;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<keybinds::BindHash> {
|
||||
CUSTOM_KEYBINDS_DLL std::size_t operator()(keybinds::BindHash const&) const;
|
||||
};
|
||||
}
|
||||
|
||||
namespace keybinds {
|
||||
class BindManager;
|
||||
class InvokeBindFilter;
|
||||
|
||||
using ActionID = std::string;
|
||||
class CUSTOM_KEYBINDS_DLL Category final {
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
Category() = default;
|
||||
Category(const char* path);
|
||||
Category(std::string const& path);
|
||||
std::vector<std::string> getPath() const;
|
||||
std::optional<Category> getParent() const;
|
||||
bool hasParent(Category const& parent) const;
|
||||
std::string toString() const;
|
||||
|
||||
bool operator==(Category const&) const;
|
||||
|
||||
static constexpr auto PLAY { "Play" };
|
||||
static constexpr auto PLAY_PAUSE { "Play/Pause" };
|
||||
static constexpr auto EDITOR { "Editor" };
|
||||
static constexpr auto GLOBAL { "Global" };
|
||||
static constexpr auto EDITOR_UI { "Editor/UI" };
|
||||
static constexpr auto EDITOR_MODIFY { "Editor/Modify" };
|
||||
static constexpr auto EDITOR_MOVE { "Editor/Move" };
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL BindableAction {
|
||||
protected:
|
||||
ActionID m_id;
|
||||
std::string m_name;
|
||||
std::string m_description;
|
||||
geode::Mod* m_owner;
|
||||
std::vector<geode::Ref<Bind>> m_defaults;
|
||||
Category m_category;
|
||||
bool m_repeatable;
|
||||
|
||||
public:
|
||||
ActionID getID() const;
|
||||
std::string getName() const;
|
||||
std::string getDescription() const;
|
||||
geode::Mod* getMod() const;
|
||||
std::vector<geode::Ref<Bind>> getDefaults() const;
|
||||
Category getCategory() const;
|
||||
bool isRepeatable() const;
|
||||
|
||||
BindableAction() = default;
|
||||
BindableAction(
|
||||
ActionID const& id,
|
||||
std::string const& name,
|
||||
std::string const& description = "",
|
||||
std::vector<geode::Ref<Bind>> const& defaults = {},
|
||||
Category const& category = Category(),
|
||||
bool repeatable = true,
|
||||
geode::Mod* owner = geode::Mod::get()
|
||||
);
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL InvokeBindEvent : public geode::Event {
|
||||
protected:
|
||||
ActionID m_id;
|
||||
bool m_down;
|
||||
|
||||
friend class BindManager;
|
||||
friend class InvokeBindFilter;
|
||||
|
||||
public:
|
||||
InvokeBindEvent(ActionID const& id, bool down);
|
||||
ActionID getID() const;
|
||||
bool isDown() const;
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL InvokeBindFilter : public geode::EventFilter<InvokeBindEvent> {
|
||||
protected:
|
||||
cocos2d::CCNode* m_target;
|
||||
ActionID m_id;
|
||||
|
||||
public:
|
||||
using Callback = geode::ListenerResult(InvokeBindEvent*);
|
||||
|
||||
geode::ListenerResult handle(std::function<Callback> fn, InvokeBindEvent* event);
|
||||
InvokeBindFilter(cocos2d::CCNode* target, ActionID const& id);
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL PressBindEvent : public geode::Event {
|
||||
protected:
|
||||
Bind* m_bind;
|
||||
bool m_down;
|
||||
|
||||
public:
|
||||
PressBindEvent(Bind* bind, bool down);
|
||||
Bind* getBind() const;
|
||||
bool isDown() const;
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL PressBindFilter : public geode::EventFilter<PressBindEvent> {
|
||||
public:
|
||||
using Callback = geode::ListenerResult(PressBindEvent*);
|
||||
|
||||
geode::ListenerResult handle(std::function<Callback> fn, PressBindEvent* event);
|
||||
PressBindFilter();
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL DeviceEvent : public geode::Event {
|
||||
protected:
|
||||
DeviceID m_id;
|
||||
bool m_attached;
|
||||
|
||||
public:
|
||||
DeviceEvent(DeviceID const& id, bool attached);
|
||||
DeviceID getID() const;
|
||||
bool wasAttached() const;
|
||||
bool wasDetached() const;
|
||||
};
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL DeviceFilter : public geode::EventFilter<DeviceEvent> {
|
||||
protected:
|
||||
std::optional<DeviceID> m_id;
|
||||
|
||||
public:
|
||||
using Callback = void(DeviceEvent*);
|
||||
|
||||
geode::ListenerResult handle(std::function<Callback> fn, DeviceEvent* event);
|
||||
DeviceFilter(std::optional<DeviceID> id = std::nullopt);
|
||||
};
|
||||
|
||||
struct CUSTOM_KEYBINDS_DLL RepeatOptions {
|
||||
bool enabled = true;
|
||||
size_t rate = 50;
|
||||
size_t delay = 500;
|
||||
};
|
||||
|
||||
using BindParser = std::function<Bind*(matjson::Value const&)>;
|
||||
|
||||
class CUSTOM_KEYBINDS_DLL BindManager : public cocos2d::CCObject {
|
||||
// has to inherit from CCObject for scheduler
|
||||
public:
|
||||
using DevicelessActions = std::unordered_map<ActionID, std::set<matjson::Value>>;
|
||||
|
||||
protected:
|
||||
struct ActionData {
|
||||
BindableAction definition;
|
||||
RepeatOptions repeat;
|
||||
};
|
||||
|
||||
std::unordered_map<BindHash, std::vector<ActionID>> m_binds;
|
||||
std::unordered_map<DeviceID, DevicelessActions> m_devicelessBinds;
|
||||
std::unordered_map<DeviceID, BindParser> m_devices;
|
||||
std::vector<std::pair<ActionID, ActionData>> m_actions;
|
||||
std::vector<Category> m_categories;
|
||||
geode::EventListener<PressBindFilter> m_listener =
|
||||
geode::EventListener<PressBindFilter>(this, &BindManager::onDispatch);
|
||||
std::unordered_map<ActionID, float> m_repeating;
|
||||
std::unordered_map<ActionID, std::unordered_set<size_t>> m_downActionBinds;
|
||||
|
||||
BindManager();
|
||||
|
||||
geode::ListenerResult onDispatch(PressBindEvent* event);
|
||||
void onRepeat(float dt);
|
||||
void repeat(ActionID const& action);
|
||||
void unrepeat(ActionID const& action);
|
||||
|
||||
bool loadActionBinds(ActionID const& action);
|
||||
void saveActionBinds(ActionID const& action);
|
||||
|
||||
void fixEmptyBindsBug();
|
||||
|
||||
/// Checks if a specific Bind + Action combo is being held.
|
||||
/// @param action the action ID we are checking
|
||||
/// @param bind the bind we are checking
|
||||
/// @returns true if the action bind is being held, false otherwise
|
||||
bool isActionBindHeld(ActionID const& action, Bind* bind);
|
||||
|
||||
/// Marks a bind that belongs to an action as held. No changes will be made if the action doesn't use the bind.
|
||||
/// @param action the action ID
|
||||
/// @param bind the bind we are marking as held, has to belong to the action
|
||||
void markActionBindHeld(ActionID const& action, Bind* bind);
|
||||
|
||||
/// Marks a bind that belongs to an action as released. No changes will be made if the action doesn't use the bind.
|
||||
/// @param action the action ID
|
||||
/// @param bind the bind we are marking as released, has to belong to the action
|
||||
void unmarkActionBindHeld(ActionID const& action, Bind* bind);
|
||||
|
||||
friend class InvokeBindFilter;
|
||||
friend struct matjson::Serialize<BindSaveData>;
|
||||
|
||||
public:
|
||||
static BindManager* get();
|
||||
void save();
|
||||
|
||||
void attachDevice(DeviceID const& device, BindParser parser);
|
||||
void detachDevice(DeviceID const& device);
|
||||
|
||||
matjson::Value saveBind(Bind* bind) const;
|
||||
Bind* loadBind(matjson::Value const& json) const;
|
||||
|
||||
bool registerBindable(BindableAction const& action, ActionID const& after = "");
|
||||
void removeBindable(ActionID const& action);
|
||||
std::optional<BindableAction> getBindable(ActionID const& action) const;
|
||||
std::vector<BindableAction> getAllBindables() const;
|
||||
std::vector<BindableAction> getBindablesIn(Category const& category, bool sub = false) const;
|
||||
std::vector<BindableAction> getBindablesFor(Bind* bind) const;
|
||||
std::vector<Category> getAllCategories() const;
|
||||
/**
|
||||
* Add a new bindable category. If the category is a subcategory (its
|
||||
* ID has a slash, like "Editor/Modify"), then all its parent
|
||||
* categories are inserted aswell, and the subcategory is added after
|
||||
* its parent's last subcategory
|
||||
* @param category The category to add. Specify a subcategory by
|
||||
* including a slash in the name (like "Editor/Modify")
|
||||
*/
|
||||
void addCategory(Category const& category);
|
||||
/**
|
||||
* @note Also removes all the bindables in this category
|
||||
*/
|
||||
void removeCategory(Category const& category);
|
||||
|
||||
void addBindTo(ActionID const& action, Bind* bind);
|
||||
void removeBindFrom(ActionID const& action, Bind* bind);
|
||||
void removeAllBindsFrom(ActionID const& action);
|
||||
void resetBindsToDefault(ActionID const& action);
|
||||
bool hasDefaultBinds(ActionID const& action) const;
|
||||
std::vector<geode::Ref<Bind>> getBindsFor(ActionID const& action) const;
|
||||
|
||||
std::optional<RepeatOptions> getRepeatOptionsFor(ActionID const& action);
|
||||
void setRepeatOptionsFor(ActionID const& action, RepeatOptions const& options);
|
||||
void stopAllRepeats();
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user