forked from geode-sdk/DevTools
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAPI.hpp
More file actions
202 lines (179 loc) · 8.69 KB
/
Copy pathAPI.hpp
File metadata and controls
202 lines (179 loc) · 8.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#pragma once
#include <Geode/loader/Event.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/ModEvent.hpp>
#include <cocos2d.h>
#include <functional>
#include <initializer_list>
#include <string>
#include <type_traits>
#include <Geode/loader/Dispatch.hpp>
#define MY_MOD_ID "geode.devtools"
namespace devtools {
template <typename T>
concept IsCCNode = std::is_base_of_v<cocos2d::CCNode, std::remove_pointer_t<T>>;
template <typename T>
concept SupportedProperty = std::is_arithmetic_v<T> ||
std::is_same_v<T, std::string> ||
std::is_same_v<T, cocos2d::ccColor3B> ||
std::is_same_v<T, cocos2d::ccColor4B> ||
std::is_same_v<T, cocos2d::ccColor4F> ||
std::is_same_v<T, cocos2d::CCPoint> ||
std::is_same_v<T, cocos2d::CCSize> ||
std::is_same_v<T, cocos2d::CCRect>;
template <typename T>
concept UnderlyingIntegral = std::is_integral_v<T> || std::is_integral_v<std::underlying_type_t<T>>;
struct RegisterNodeEvent final : geode::SimpleEvent<RegisterNodeEvent, geode::Function<void(cocos2d::CCNode*)>> {
using SimpleEvent::SimpleEvent;
};
template <typename T>
struct PropertyFnEvent final : geode::SimpleEvent<PropertyFnEvent<T>, bool(*&)(const char* name, T&)> {
using Fn = bool(const char* name, T&);
using geode::SimpleEvent<PropertyFnEvent, Fn*&>::SimpleEvent;
};
struct DrawLabelFnEvent final : geode::SimpleEvent<DrawLabelFnEvent, void(*&)(const char* text)> {
using Fn = void(const char* text);
using SimpleEvent::SimpleEvent;
};
template <typename T>
struct EnumerableFnEvent final : geode::SimpleEvent<EnumerableFnEvent<T>, bool(*&)(const char* label, T* value, std::span<std::pair<T, const char*> const>)> {
using Fn = bool(const char* label, T* value, std::span<std::pair<T, const char*> const>);
using geode::SimpleEvent<EnumerableFnEvent, Fn*&>::SimpleEvent;
};
struct ButtonFnEvent final : geode::SimpleEvent<ButtonFnEvent, bool(*&)(const char* label)> {
using Fn = bool(const char* label);
using SimpleEvent::SimpleEvent;
};
/// @brief Checks if DevTools is currently loaded.
/// @return True if DevTools is loaded, false otherwise.
inline bool isLoaded() {
return geode::Loader::get()->getLoadedMod("geode.devtools") != nullptr;
}
/// @brief Waits for DevTools to be loaded and then calls the provided callback.
/// @param callback The function to call once DevTools is loaded.
template <typename F>
void waitForDevTools(F&& callback) {
if (isLoaded()) {
callback();
} else {
auto devtools = geode::Loader::get()->getInstalledMod("geode.devtools");
if (!devtools) return;
geode::ModStateEvent(geode::ModEventType::Loaded, devtools).listen(
[callback = std::forward<F>(callback)]() {
callback();
}
).leak();
}
}
/// @brief Registers a callback that will be called whenever a node of type T is opened in Attributes tab.
/// @param callback The function to call with the node when it is opened.
/// @see `devtools::property`, `devtools::label`, `devtools::enumerable`, `devtools::button`
template <typename T, std::invocable<std::remove_pointer_t<T>*> F> requires IsCCNode<T>
void registerNode(F&& callback) {
RegisterNodeEvent().send([callback = std::forward<F>(callback)](cocos2d::CCNode* node) {
if (auto casted = geode::cast::typeinfo_cast<std::remove_pointer_t<T>*>(node)) {
callback(casted);
}
});
}
/// @brief Renders a property editor for the given value in the DevTools UI.
/// @param name The name of the property to display.
/// @param prop The property value to edit.
/// @return True if the property was changed, false otherwise.
/// @warning This function should only ever be called from within a registered node callback.
template <typename T> requires SupportedProperty<T>
bool property(const char* name, T& prop) {
static auto fn = ([] {
typename PropertyFnEvent<T>::Fn* fnPtr = nullptr;
PropertyFnEvent<T>().send(fnPtr);
return fnPtr;
})();
return fn ? fn(name, prop) : false;
}
/// @brief Renders a label in the DevTools UI.
/// @param text The text to display in the label.
/// @warning This function should only ever be called from within a registered node callback.
inline void label(const char* text) {
static auto fn = ([] {
DrawLabelFnEvent::Fn* fnPtr = nullptr;
DrawLabelFnEvent().send(fnPtr);
return fnPtr;
})();
if (fn) fn(text);
}
/// @brief Renders an enumerable property editor using radio buttons for the given value in the DevTools UI.
/// @param label The label for the enumerable property.
/// @param value The value to edit, which should be an enum or integral type.
/// @param items A list of pairs where each pair contains a value and its corresponding label.
/// @return True if the value was changed, false otherwise.
/// @warning This function should only ever be called from within a registered node callback.
template <UnderlyingIntegral T>
bool enumerable(const char* label, T& value, std::initializer_list<std::pair<T, const char*>> items) {
using ValueType = std::underlying_type_t<T>;
static auto fn = ([] {
typename EnumerableFnEvent<ValueType>::Fn* fnPtr = nullptr;
EnumerableFnEvent<ValueType>().send(fnPtr);
return fnPtr;
})();
return fn ? fn(
label,
reinterpret_cast<ValueType*>(&value),
std::span(
reinterpret_cast<std::pair<ValueType, const char*> const*>(&*items.begin()),
reinterpret_cast<std::pair<ValueType, const char*> const*>(&*items.end())
)
) : false;
}
/// @brief Renders a button in the DevTools UI.
/// @param label The label for the button.
/// @return True if the button was clicked, false otherwise.
/// @warning This function should only ever be called from within a registered node callback.
inline bool button(const char* label) {
static auto fn = ([] {
ButtonFnEvent::Fn* fnPtr = nullptr;
ButtonFnEvent().send(fnPtr);
return fnPtr;
})();
return fn ? fn(label) : false;
}
/// @brief Renders a button in the DevTools UI and calls the provided callback if the button is clicked.
/// @param label The label for the button.
/// @param callback The function to call when the button is clicked.
/// @warning This function should only ever be called from within a registered node callback.
template <typename F>
void button(const char* label, F&& callback) {
if (button(label)) {
callback();
}
}
inline void newLine() GEODE_EVENT_EXPORT_NORES(&newLine, ());
inline void sameLine() GEODE_EVENT_EXPORT_NORES(&sameLine, ());
inline void separator() GEODE_EVENT_EXPORT_NORES(&separator, ());
inline void nextItemWidth(float width) GEODE_EVENT_EXPORT_NORES(&nextItemWidth, (width));
inline void indent() GEODE_EVENT_EXPORT_NORES(&indent, ());
inline void unindent() GEODE_EVENT_EXPORT_NORES(&unindent, ());
inline bool combo(
char const* label,
int& current,
std::span<char const*> items,
int maxHeight = -1
) GEODE_EVENT_EXPORT_NORES(&combo, (label, current, items, maxHeight));
template <UnderlyingIntegral T, typename R = std::initializer_list<char const*>>
requires
std::ranges::range<R> &&
std::same_as<std::remove_pointer_t<decltype(&*std::declval<R>().begin())> const, char const* const>
bool combo(char const* label, T& current, R&& range, int maxHeight = -1) {
return combo(
label,
reinterpret_cast<int&>(current),
std::span(const_cast<char const**>(&*range.begin()), const_cast<char const**>(&*range.end())),
maxHeight
);
}
inline bool radio(char const* label, int& current, int num) GEODE_EVENT_EXPORT_NORES(&radio, (label, current, num));
template <UnderlyingIntegral T, UnderlyingIntegral U>
bool radio(char const* label, T& current, U value) {
return radio(label, reinterpret_cast<int&>(current), reinterpret_cast<int&>(value));
}
inline void inputMultiline(char const* label, std::string& text) GEODE_EVENT_EXPORT_NORES(&inputMultiline, (label, text));
}