Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 23 additions & 47 deletions include/API.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,20 @@ namespace devtools {
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;
struct RegisterNodeEvent final : geode::Event<RegisterNodeEvent, bool(geode::Function<void(cocos2d::CCNode*)>)> {
using Event::Event;
};

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;
struct PropertyFnEvent final : geode::Event<PropertyFnEvent<T>, bool(bool(*&)(geode::ZStringView name, T&))> {
using Fn = bool(geode::ZStringView name, T&);
using geode::Event<PropertyFnEvent, bool(Fn*&)>::Event;
};

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;
struct EnumerableFnEvent final : geode::Event<EnumerableFnEvent<T>, bool(bool(*&)(geode::ZStringView label, T* value, std::span<std::pair<T, geode::ZStringView> const>))> {
using Fn = bool(geode::ZStringView label, T* value, std::span<std::pair<T, geode::ZStringView> const>);
using geode::Event<EnumerableFnEvent, bool(Fn*&)>::Event;
};

/// @brief Checks if DevTools is currently loaded.
Expand Down Expand Up @@ -95,7 +85,7 @@ namespace devtools {
/// @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) {
bool property(geode::ZStringView name, T& prop) {
static auto fn = ([] {
typename PropertyFnEvent<T>::Fn* fnPtr = nullptr;
PropertyFnEvent<T>().send(fnPtr);
Expand All @@ -107,14 +97,7 @@ namespace devtools {
/// @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);
}
inline void label(geode::ZStringView text) GEODE_EVENT_EXPORT_NORES(&label, (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.
Expand All @@ -123,7 +106,7 @@ namespace devtools {
/// @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) {
bool enumerable(geode::ZStringView label, T& value, std::initializer_list<std::pair<T, geode::ZStringView>> items) {
using ValueType = std::underlying_type_t<T>;
static auto fn = ([] {
typename EnumerableFnEvent<ValueType>::Fn* fnPtr = nullptr;
Expand All @@ -134,8 +117,8 @@ namespace devtools {
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())
reinterpret_cast<std::pair<ValueType, geode::ZStringView> const*>(&*items.begin()),
reinterpret_cast<std::pair<ValueType, geode::ZStringView> const*>(&*items.end())
)
) : false;
}
Expand All @@ -144,21 +127,14 @@ namespace devtools {
/// @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;
}
inline bool button(geode::ZStringView label) GEODE_EVENT_EXPORT_NORES(&button, (label));

/// @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) {
void button(geode::ZStringView label, F&& callback) {
if (button(label)) {
callback();
}
Expand All @@ -172,31 +148,31 @@ namespace devtools {
inline void unindent() GEODE_EVENT_EXPORT_NORES(&unindent, ());

inline bool combo(
char const* label,
geode::ZStringView 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*>>
template <UnderlyingIntegral T, typename R = std::initializer_list<geode::ZStringView>>
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) {
std::same_as<std::remove_pointer_t<decltype(&*std::declval<R>().begin())> const, geode::ZStringView const>
bool combo(geode::ZStringView 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())),
std::span(const_cast<geode::ZStringView*>(&*range.begin()), const_cast<geode::ZStringView*>(&*range.end())),
maxHeight
);
}

inline bool radio(char const* label, int& current, int num) GEODE_EVENT_EXPORT_NORES(&radio, (label, current, num));
inline bool radio(geode::ZStringView 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) {
bool radio(geode::ZStringView 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));
inline void inputMultiline(geode::ZStringView label, std::string& text) GEODE_EVENT_EXPORT_NORES(&inputMultiline, (label, text));
}
78 changes: 35 additions & 43 deletions src/API.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ static void handleType() {
sizeof(T) == 2 ? (isSigned ? ImGuiDataType_S16 : ImGuiDataType_U16) :
sizeof(T) == 4 ? (isSigned ? ImGuiDataType_S32 : ImGuiDataType_U32) :
isSigned ? ImGuiDataType_S64 : ImGuiDataType_U64;
fnPtr = +[](const char* name, T& prop) {
return ImGui::DragScalar(name, dataType, &prop);
fnPtr = +[](ZStringView name, T& prop) {
return ImGui::DragScalar(name.c_str(), dataType, &prop);
};
return ListenerResult::Stop;
}).leak();

devtools::EnumerableFnEvent<T>().listen([](typename devtools::EnumerableFnEvent<T>::Fn*& fnPtr) {
fnPtr = +[](const char* label, T* value, std::span<std::pair<T, const char*> const> items) {
ImGui::Text("%s:", label);
fnPtr = +[](ZStringView label, T* value, std::span<std::pair<T, ZStringView> const> items) {
ImGui::Text("%s:", label.c_str());
size_t i = 0;
bool changed = false;
for (auto& [itemValue, itemLabel] : items) {
if (ImGui::RadioButton(itemLabel, *value == itemValue)) {
if (ImGui::RadioButton(itemLabel.c_str(), *value == itemValue)) {
*value = itemValue;
changed = true;
}
Expand Down Expand Up @@ -62,20 +62,28 @@ void devtools::indent() {
void devtools::unindent() {
ImGui::Unindent(16.f);
}
bool devtools::combo(char const* label, int& current, std::span<char const*> items, int maxHeight) {
bool devtools::combo(ZStringView label, int& current, std::span<char const*> items, int maxHeight) {
return ImGui::Combo(
label,
label.c_str(),
&current,
&*items.begin(),
static_cast<int>(items.size()),
maxHeight
);
}
bool devtools::radio(const char* label, int& current, int num) {
return ImGui::RadioButton(label, &current, num);
bool devtools::radio(ZStringView label, int& current, int num) {
return ImGui::RadioButton(label.c_str(), &current, num);
}
void devtools::inputMultiline(const char* label, std::string& str) {
ImGui::InputTextMultiline(label, &str);
void devtools::inputMultiline(ZStringView label, std::string& str) {
ImGui::InputTextMultiline(label.c_str(), &str);
}

void devtools::label(ZStringView text) {
ImGui::Text("%s", text.c_str());
}

bool devtools::button(ZStringView label) {
return ImGui::Button(label.c_str());
}

$execute {
Expand All @@ -100,30 +108,30 @@ void devtools::inputMultiline(const char* label, std::string& str) {

// checkbox
devtools::PropertyFnEvent<bool>().listen([](devtools::PropertyFnEvent<bool>::Fn*& fnPtr) {
fnPtr = +[](const char* name, bool& prop) {
return ImGui::Checkbox(name, &prop);
fnPtr = +[](ZStringView name, bool& prop) {
return ImGui::Checkbox(name.c_str(), &prop);
};
return ListenerResult::Stop;
}).leak();

// string
devtools::PropertyFnEvent<std::string>().listen([](devtools::PropertyFnEvent<std::string>::Fn*& fnPtr) {
fnPtr = +[](const char* name, std::string& prop) {
return ImGui::InputText(name, &prop);
fnPtr = +[](ZStringView name, std::string& prop) {
return ImGui::InputText(name.c_str(), &prop);
};
return ListenerResult::Stop;
}).leak();

// colors
devtools::PropertyFnEvent<ccColor3B>().listen([](devtools::PropertyFnEvent<ccColor3B>::Fn*& fnPtr) {
fnPtr = +[](const char* name, ccColor3B& prop) {
fnPtr = +[](ZStringView name, ccColor3B& prop) {
auto color = ImVec4(
prop.r / 255.f,
prop.g / 255.f,
prop.b / 255.f,
1.0f
);
if (ImGui::ColorEdit3(name, &color.x)) {
if (ImGui::ColorEdit3(name.c_str(), &color.x)) {
prop.r = static_cast<GLubyte>(color.x * 255);
prop.g = static_cast<GLubyte>(color.y * 255);
prop.b = static_cast<GLubyte>(color.z * 255);
Expand All @@ -134,14 +142,14 @@ void devtools::inputMultiline(const char* label, std::string& str) {
return ListenerResult::Stop;
}).leak();
devtools::PropertyFnEvent<ccColor4B>().listen([](devtools::PropertyFnEvent<ccColor4B>::Fn*& fnPtr) {
fnPtr = +[](const char* name, ccColor4B& prop) {
fnPtr = +[](ZStringView name, ccColor4B& prop) {
auto color = ImVec4(
prop.r / 255.f,
prop.g / 255.f,
prop.b / 255.f,
prop.a / 255.f
);
if (ImGui::ColorEdit4(name, &color.x)) {
if (ImGui::ColorEdit4(name.c_str(), &color.x)) {
prop.r = static_cast<GLubyte>(color.x * 255);
prop.g = static_cast<GLubyte>(color.y * 255);
prop.b = static_cast<GLubyte>(color.z * 255);
Expand All @@ -153,45 +161,29 @@ void devtools::inputMultiline(const char* label, std::string& str) {
return ListenerResult::Stop;
}).leak();
devtools::PropertyFnEvent<ccColor4F>().listen([](devtools::PropertyFnEvent<ccColor4F>::Fn*& fnPtr) {
fnPtr = +[](const char* name, ccColor4F& prop) {
return ImGui::ColorEdit4(name, reinterpret_cast<float*>(&prop));
fnPtr = +[](ZStringView name, ccColor4F& prop) {
return ImGui::ColorEdit4(name.c_str(), reinterpret_cast<float*>(&prop));
};
return ListenerResult::Stop;
}).leak();

// points/sizes
devtools::PropertyFnEvent<CCPoint>().listen([](devtools::PropertyFnEvent<CCPoint>::Fn*& fnPtr) {
fnPtr = +[](const char* name, CCPoint& prop) {
return ImGui::DragFloat2(name, reinterpret_cast<float*>(&prop));
fnPtr = +[](ZStringView name, CCPoint& prop) {
return ImGui::DragFloat2(name.c_str(), reinterpret_cast<float*>(&prop));
};
return ListenerResult::Stop;
}).leak();

devtools::PropertyFnEvent<CCSize>().listen([](devtools::PropertyFnEvent<CCSize>::Fn*& fnPtr) {
fnPtr = +[](const char* name, CCSize& prop) {
return ImGui::DragFloat2(name, reinterpret_cast<float*>(&prop));
fnPtr = +[](ZStringView name, CCSize& prop) {
return ImGui::DragFloat2(name.c_str(), reinterpret_cast<float*>(&prop));
};
return ListenerResult::Stop;
}).leak();
devtools::PropertyFnEvent<CCRect>().listen([](devtools::PropertyFnEvent<CCRect>::Fn*& fnPtr) {
fnPtr = +[](const char* name, CCRect& prop) {
return ImGui::DragFloat4(name, reinterpret_cast<float*>(&prop));
};
return ListenerResult::Stop;
}).leak();

// label
devtools::DrawLabelFnEvent().listen([](devtools::DrawLabelFnEvent::Fn*& fnPtr) {
fnPtr = +[](const char* text) {
ImGui::Text("%s", text);
};
return ListenerResult::Stop;
}).leak();

// button
devtools::ButtonFnEvent().listen([](devtools::ButtonFnEvent::Fn*& fnPtr) {
fnPtr = +[](const char* label) {
return ImGui::Button(label);
fnPtr = +[](ZStringView name, CCRect& prop) {
return ImGui::DragFloat4(name.c_str(), reinterpret_cast<float*>(&prop));
};
return ListenerResult::Stop;
}).leak();
Expand Down
6 changes: 3 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class $modify(CCDirector) {
if (!DevTools::get()->shouldUseGDWindow()) {
return CCDirector::drawScene();
}

DevTools::get()->setup();

static GLRenderCtx* gdTexture = nullptr;
Expand Down Expand Up @@ -153,8 +153,8 @@ class $modify(CCEGLView) {
};

// For the one eclipse shortcut
struct ToggleDevToolsEvent : SimpleEvent<ToggleDevToolsEvent> {
using SimpleEvent::SimpleEvent;
struct ToggleDevToolsEvent : Event<ToggleDevToolsEvent, bool()> {
using Event::Event;
};

$execute {
Expand Down
18 changes: 3 additions & 15 deletions src/pages/Advanced.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,7 @@ ModMetadata DevTools::inputMetadata(void* treePtr, ModMetadata metadata) {
continue;
}
ImGui::Text("version: %s", item.getVersion().toString().c_str());
const char* importance = "";
switch (item.getImportance()) {
case geode::ModMetadata::Dependency::Importance::Required: importance = "required"; break;
case geode::ModMetadata::Dependency::Importance::Recommended: importance = "recommended"; break;
case geode::ModMetadata::Dependency::Importance::Suggested: importance = "suggested"; break;
}
ImGui::Text("importance: %s", importance);
ImGui::Text("required: %s", item.isRequired() ? "true" : "false");
ImGui::Text("isResolved: %s", item.isResolved() ? "true" : "false");
if (item.getMod())
drawModGraphNode(item.getMod());
Expand All @@ -120,13 +114,7 @@ ModMetadata DevTools::inputMetadata(void* treePtr, ModMetadata metadata) {
continue;
}
ImGui::Text("version: %s", item.getVersion().toString().c_str());
const char* importance = "";
switch (item.getImportance()) {
case geode::ModMetadata::Incompatibility::Importance::Breaking: importance = "breaking"; break;
case geode::ModMetadata::Incompatibility::Importance::Conflicting: importance = "conflicting"; break;
case geode::ModMetadata::Incompatibility::Importance::Superseded: importance = "superseded"; break;
}
ImGui::Text("importance: %s", importance);
ImGui::Text("breaking: %s", item.isBreaking() ? "true" : "false");
ImGui::Text("isResolved: %s", item.isResolved() ? "true" : "false");
if (item.getMod())
drawModGraphNode(item.getMod());
Expand Down Expand Up @@ -165,7 +153,7 @@ void DevTools::drawModGraphNode(Mod* node) {
ImColor color = ImColor(1.f, 1.f, 1.f);
if (node->isUninstalled())
color = ImColor(0.1f, 0.1f, 0.1f);
else if (!node->isEnabled())
else if (!node->isLoaded())
color = ImColor(0.7f, 0.7f, 0.7f);

ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)color);
Expand Down