yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
widget_id_registry.h
Go to the documentation of this file.
1#ifndef YAZE_APP_GUI_AUTOMATION_WIDGET_ID_REGISTRY_H_
2#define YAZE_APP_GUI_AUTOMATION_WIDGET_ID_REGISTRY_H_
3
4#include <cstdint>
5#include <optional>
6#include <string>
7#include <unordered_map>
8#include <vector>
9
10#include "absl/strings/string_view.h"
11#include "absl/synchronization/mutex.h"
12#include "absl/time/time.h"
13#include "imgui/imgui.h"
14
15namespace yaze {
16namespace gui {
17
38 public:
39 explicit WidgetIdScope(const std::string& name);
41
42 // Get the full hierarchical path at this scope level
43 std::string GetFullPath() const;
44
45 // Get the full path with a widget suffix
46 std::string GetWidgetPath(const std::string& widget_type,
47 const std::string& widget_name) const;
48
49 private:
50 std::string name_;
51 static thread_local std::vector<std::string> id_stack_;
52};
53
69 public:
70 struct WidgetBounds {
71 float min_x = 0.0f;
72 float min_y = 0.0f;
73 float max_x = 0.0f;
74 float max_y = 0.0f;
75 bool valid = false;
76 };
77
79 std::optional<std::string> label;
80 std::optional<std::string> window_name;
81 std::optional<bool> visible;
82 std::optional<bool> enabled;
83 std::optional<WidgetBounds> bounds;
84 };
85
86 struct WidgetInfo {
87 std::string full_path; // e.g. "Overworld/Canvas/canvas:Map"
88 std::string type; // e.g. "button", "input", "canvas", "table"
89 ImGuiID imgui_id; // ImGui's internal ID
90 std::string description; // Optional human-readable description
91 std::string label; // Sanitized display label (without IDs/icons)
92 std::string window_name; // Window this widget was last seen in
93 bool visible = true; // Visibility in the most recent frame
94 bool enabled = true; // Enabled state in the most recent frame
95 WidgetBounds bounds; // Bounding box in screen space
97 absl::Time last_seen_time;
100 };
101
102 static WidgetIdRegistry& Instance();
103
104 // Frame lifecycle - call once per ImGui frame
105 void BeginFrame();
106 void EndFrame();
107
108 // Register a widget for discovery
109 // Should be called after widget is created (when ImGui::GetItemID() is valid)
110 void RegisterWidget(const std::string& full_path, const std::string& type,
111 ImGuiID imgui_id, const std::string& description = "",
112 const WidgetMetadata& metadata = WidgetMetadata());
113
114 // Query widgets for test automation
115 std::vector<std::string> FindWidgets(const std::string& pattern) const;
116 ImGuiID GetWidgetId(const std::string& full_path) const;
117 std::optional<WidgetInfo> GetWidgetInfo(const std::string& full_path) const;
118
119 // Get a thread-safe snapshot of all registered widgets.
120 std::unordered_map<std::string, WidgetInfo> GetAllWidgets() const;
121
122 // Clear all registered widgets (useful between frames for dynamic UIs)
123 void Clear();
124
125 // Export catalog for z3ed agent describe
126 // Format: "yaml" or "json"
127 std::string ExportCatalog(const std::string& format = "yaml") const;
128 void ExportCatalogToFile(const std::string& output_file,
129 const std::string& format = "yaml") const;
130
131 // Helper utilities for consistent naming
132 static std::string NormalizeLabel(absl::string_view label);
133 static std::string NormalizePathSegment(absl::string_view segment);
134
135 private:
136 WidgetIdRegistry() = default;
137 void TrimStaleEntries();
138 bool ShouldPrune(const WidgetInfo& info) const;
139
140 mutable absl::Mutex mutex_;
141 std::unordered_map<std::string, WidgetInfo> widgets_ ABSL_GUARDED_BY(mutex_);
142 int current_frame_ ABSL_GUARDED_BY(mutex_) = -1;
143 absl::Time frame_time_ ABSL_GUARDED_BY(mutex_);
144 int stale_frame_limit_ = 600; // frames before pruning a widget
145};
146
147// RAII helper macros for convenient scoping
148#define YAZE_WIDGET_SCOPE(name) \
149 yaze::gui::WidgetIdScope _yaze_scope_##__LINE__(name)
150
151// Register a widget after creation (when GetItemID() is valid)
152#define YAZE_REGISTER_WIDGET(widget_type, widget_name) \
153 do { \
154 if (ImGui::GetItemID() != 0) { \
155 yaze::gui::WidgetIdRegistry::Instance().RegisterWidget( \
156 _yaze_scope_##__LINE__.GetWidgetPath(#widget_type, widget_name), \
157 #widget_type, ImGui::GetItemID()); \
158 } \
159 } while (0)
160
161// Convenience macro for registering with automatic name extraction
162// Usage: YAZE_REGISTER_CURRENT_WIDGET("button")
163#define YAZE_REGISTER_CURRENT_WIDGET(widget_type) \
164 do { \
165 if (ImGui::GetItemID() != 0) { \
166 yaze::gui::WidgetIdRegistry::Instance().RegisterWidget( \
167 _yaze_scope_##__LINE__.GetWidgetPath(widget_type, \
168 ImGui::GetLastItemLabel()), \
169 widget_type, ImGui::GetItemID()); \
170 } \
171 } while (0)
172
173} // namespace gui
174} // namespace yaze
175
176#endif // YAZE_APP_GUI_AUTOMATION_WIDGET_ID_REGISTRY_H_
Centralized registry for discoverable GUI widgets.
std::optional< WidgetInfo > GetWidgetInfo(const std::string &full_path) const
absl::Time frame_time_ ABSL_GUARDED_BY(mutex_)
void ExportCatalogToFile(const std::string &output_file, const std::string &format="yaml") const
static std::string NormalizePathSegment(absl::string_view segment)
std::string ExportCatalog(const std::string &format="yaml") const
bool ShouldPrune(const WidgetInfo &info) const
int current_frame_ ABSL_GUARDED_BY(mutex_)
ImGuiID GetWidgetId(const std::string &full_path) const
static std::string NormalizeLabel(absl::string_view label)
void RegisterWidget(const std::string &full_path, const std::string &type, ImGuiID imgui_id, const std::string &description="", const WidgetMetadata &metadata=WidgetMetadata())
std::unordered_map< std::string, WidgetInfo > widgets_ ABSL_GUARDED_BY(mutex_)
std::unordered_map< std::string, WidgetInfo > GetAllWidgets() const
static WidgetIdRegistry & Instance()
std::vector< std::string > FindWidgets(const std::string &pattern) const
RAII helper for managing hierarchical ImGui ID scopes.
WidgetIdScope(const std::string &name)
std::string GetFullPath() const
static thread_local std::vector< std::string > id_stack_
std::string GetWidgetPath(const std::string &widget_type, const std::string &widget_name) const