Unravel Engine C++ Reference
Loading...
Searching...
No Matches
inspector.cpp
Go to the documentation of this file.
1#include "inspector.h"
3#include <imgui/imgui_internal.h>
6#include <engine/ecs/scene.h>
7#include <engine/engine.h>
9#include "imgui/imgui.h"
10#include "inspectors.h"
11
12namespace ImGui
13{
14 auto GetTintedStyleColor(ImGuiCol idx, float multiplier = 1.0f) -> ImVec4
15 {
16 auto color = ImGui::GetStyleColorVec4(idx);
17 color.x *= multiplier;
18 color.y *= multiplier;
19 color.z *= multiplier;
20 return color;
21 }
22}
23
24namespace unravel
25{
26
27namespace
28{
29std::vector<property_layout*> stack;
30void push_layout_to_stack(property_layout* l)
31{
32 stack.push_back(l);
33}
34
35void pop_layout_from_stack(property_layout* l)
36{
37 stack.pop_back();
38}
39} // namespace
40
41
43{
44 // ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetTintedStyleColor(ImGuiCol_ChildBg, 0.8f));
45 // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
46 // ImGui::BeginChild(name.c_str(), {-1.0f, 0.0f}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY);
47
48}
49
51{
52 // ImGui::EndChild();
53 // ImGui::PopStyleVar();
54 // ImGui::PopStyleColor();
55 // ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImGuiCol_Separator), 2.0f);
56
57}
58
60{
61 if(stack.empty())
62 {
63 return nullptr;
64 }
65 return stack.back();
66}
67
69{
70 push_layout_to_stack(this);
71}
72
73property_layout::property_layout(const entt::meta_data& prop, const entt::meta_any& object, bool columns /*= true*/)
74{
75 push_layout_to_stack(this);
76
77 set_data(prop, object, columns);
78
80}
81
82property_layout::property_layout(const std::string& name, bool columns /*= true*/)
83{
84 push_layout_to_stack(this);
85
86 set_data(name, "", "", columns);
87
89}
90
91property_layout::property_layout(const std::string& name, const std::string& tooltip, bool columns /*= true*/)
92{
93 push_layout_to_stack(this);
94
95 set_data(name, tooltip, "", columns);
96
98}
99
100property_layout::property_layout(const std::string& name, const std::string& tooltip, const std::string& hint, bool columns /*= true*/)
101{
102 push_layout_to_stack(this);
103
104 set_data(name, tooltip, hint, columns);
105
106 push_layout();
107}
108
109property_layout::property_layout(const std::string& name, const std::function<void()>& callback, bool columns /*= true*/)
110{
111 push_layout_to_stack(this);
112
113 callback_ = callback;
114 set_data(name, "", "", columns);
115
116 push_layout();
117}
118
119
121{
122 pop_layout();
123
124 pop_layout_from_stack(this);
125}
126
127void property_layout::set_data(const entt::meta_data& prop, const entt::meta_any& object, bool columns)
128{
129 auto name = entt::get_pretty_name(prop);
130
131 auto hint = entt::get_property_hint(object, prop);
132
133 auto tooltip = entt::get_attribute_as<std::string>(prop, "tooltip");
134
135 set_data(name, tooltip, hint, columns);
136
137}
138
139void property_layout::set_data(const std::string& name, const std::string& tooltip, const std::string& hint, bool columns)
140{
142 tooltip_ = tooltip;
143 hint_ = hint;
144 columns_ = columns;
145}
146
147void property_layout::push_layout(bool auto_proceed_to_next_column)
148{
149 pushed_ = true;
150
151 if(columns_)
152 {
153 auto avail = ImGui::GetContentRegionAvail();
154
155 columns_open_ = ImGui::BeginTable(("properties##" + name_).c_str(), 2);
156
157 if(columns_open_)
158 {
159
160 auto first_column = 0.325f;
161 ImGui::TableSetupColumn("##prop_column1", ImGuiTableColumnFlags_WidthFixed, avail.x * first_column);
162 ImGui::TableSetupColumn("##prop_column2", ImGuiTableColumnFlags_WidthFixed, avail.x * (1.0f - first_column));
163
164 ImGui::TableNextRow();
165 ImGui::TableNextColumn();
166 }
167 }
168
169 ImGui::AlignTextToFramePadding();
170 if(callback_)
171 {
172 callback_();
173 }
174 else
175 {
176 ImGui::TextUnformatted(name_.c_str());
177 }
178
179
180
181 if(ImGui::BeginPopupContextItem(("Property Context Menu##" + name_).c_str()))
182 {
183 {
185
186 auto& ctx = engine::context();
187 auto& override_ctx = ctx.get_cached<prefab_override_context>();
188
189 if(override_ctx.is_path_overridden())
190 {
191 if(ImGui::MenuItem(fmt::format("Reset {} to default", name_).c_str()))
192 {
193 override_ctx.reset_override();
194 }
195 }
196 }
197 ImGui::EndPopup();
198 }
199
200 if(!tooltip_.empty())
201 {
202
203 auto tooltip_callback = [&]()
204 {
205 ImGui::PushStyleColor(ImGuiCol_Separator, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled));
208 ImGui::Text("%s", name_.c_str());
210 ImGui::PopFont();
211 ImGui::Spacing();
212 ImGui::Separator();
213 ImGui::Spacing();
214 ImGui::Text("%s", tooltip_.c_str());
215 ImGui::PopStyleColor();
216 };
217 ImGui::ItemTooltipEx(tooltip_callback);
218
219 ImGui::SameLine();
220 ImGui::HelpMarker("(?)", true, tooltip_callback);
221 }
222
223 if(auto_proceed_to_next_column)
224 {
226 }
227}
228
230{
231 if(columns_open_)
232 {
233 ImGui::TableNextColumn();
234 }
235
236 ImGui::PushID(name_.c_str());
237 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
238}
239
240auto property_layout::push_tree_layout(ImGuiTreeNodeFlags flags, bool default_open) -> bool
241{
242 pushed_ = true;
243
244 // group_ = property_layout_group(name_);
245
246 if(columns_)
247 {
248 auto avail = ImGui::GetContentRegionAvail();
249
250 columns_open_ = ImGui::BeginTable(("properties##" + name_).c_str(), 2);
251
252 if(columns_open_)
253 {
254 auto first_column = 0.325f;
255 ImGui::TableSetupColumn("##prop_column1", ImGuiTableColumnFlags_WidthFixed, avail.x * first_column);
256 ImGui::TableSetupColumn("##prop_column2", ImGuiTableColumnFlags_WidthFixed, avail.x * (1.0f - first_column));
257
258 ImGui::TableNextRow();
259 ImGui::TableNextColumn();
260 }
261 }
262
263 ImGui::SetNextItemOpen(default_open, ImGuiCond_Appearing);
264 ImGui::AlignTextToFramePadding();
265 // ImGui::PushFont(ImGui::Font::Bold);
266 open_ = ImGui::TreeNodeEx(name_.c_str(), flags | ImGuiTreeNodeFlags_AllowOverlap);
267 // open_ = ImGui::CollapsingSection(name_.c_str(), flags | ImGuiTreeNodeFlags_AllowOverlap);
268 // ImGui::PopFont();
269
270 if(ImGui::BeginPopupContextItem(("Property Context Menu##" + name_).c_str()))
271 {
272 {
274
275 auto& ctx = engine::context();
276 auto& override_ctx = ctx.get_cached<prefab_override_context>();
277
278 if(override_ctx.is_path_overridden())
279 {
280 if(ImGui::MenuItem(fmt::format("Reset {} to default", name_).c_str()))
281 {
282 override_ctx.reset_override();
283 }
284 }
285 }
286 ImGui::EndPopup();
287 }
288
289 if(!tooltip_.empty())
290 {
291
292 auto tooltip_callback = [&]()
293 {
294 ImGui::PushStyleColor(ImGuiCol_Separator, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled));
297 ImGui::Text("%s", name_.c_str());
299 ImGui::PopFont();
300 ImGui::Spacing();
301 ImGui::Separator();
302 ImGui::Spacing();
303 ImGui::Text("%s", tooltip_.c_str());
304 ImGui::PopStyleColor();
305 };
306 ImGui::ItemTooltipEx(tooltip_callback);
307
308 ImGui::SameLine();
309 ImGui::HelpMarker("(?)", true, tooltip_callback);
310 }
311
312 prepare_for_item();
313
314 return open_;
315}
316
318{
319 if(!pushed_)
320 {
321 return;
322 }
323
324 ImGui::PopID();
325 ImGui::PopItemWidth();
326
327 if(open_)
328 {
329 open_ = false;
330 ImGui::TreePop();
331 }
332
333 if(columns_)
334 {
335 columns_ = false;
336 if(columns_open_ && ImGui::TableGetColumnCount() > 1)
337 {
338 ImGui::EndTable();
339 }
340 }
341
342
343
344
345 if(!hint_.empty())
346 {
347 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x);
348 ImGui::TextColored(ImVec4(1.0f, 0.62f, 0.0f, 1.0f), "%s",hint_.c_str());
349 ImGui::PopTextWrapPos();
350 }
351 // group_.reset();
352
353 pushed_ = false;
354}
355
356void inspector::before_inspect(const entt::meta_data& prop, const entt::meta_any& object)
357{
358 layout_ = std::make_unique<property_layout>(prop, object);
359}
360
361void inspector::after_inspect(const entt::meta_data& prop, const entt::meta_any& object)
362{
363 layout_.reset();
364}
365
366
367auto make_proxy(entt::meta_any& var, const std::string& name) -> meta_any_proxy
368{
369 meta_any_proxy proxy;
370 proxy.impl->parent = nullptr;
371 proxy.impl->type_name = entt::get_pretty_name(var.type());
372 proxy.impl->name = name;
373 proxy.impl->resolver = [var](entt::meta_any& result) mutable
374 {
375 result = var;
376 return true;
377 };
378 proxy.impl->getter = [var = var.as_ref()](entt::meta_any& result) mutable
379 {
380 result = var.as_ref();
381 return true;
382 };
383 proxy.impl->setter = [](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
384 {
385 entt::meta_any var;
386 if(proxy.impl->getter(var) && var)
387 {
388 return var.assign(value);
389 }
390 return false;
391 };
392 return proxy;
393}
394
395auto make_entity_proxy(entt::meta_any& var, const std::string& name) -> meta_any_proxy
396{
397 if(var.type() != entt::resolve<entt::uhandle>())
398 {
399 return make_proxy(var, name);
400 }
401 auto handle = var.cast<entt::uhandle>();
402
403 meta_any_proxy proxy;
404 proxy.impl->parent = nullptr;
405 proxy.impl->type_name = entt::get_pretty_name(var.type());
406 proxy.impl->name = name;
407 proxy.impl->resolver = [handle](entt::meta_any& result) mutable -> bool
408 {
409 result = handle;
410 return !!result;
411 };
412 proxy.impl->getter = [handle](entt::meta_any& result) mutable -> bool
413 {
414 result = handle;
415 return !!result;
416 };
417 proxy.impl->setter = [](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
418 {
419 entt::meta_any var_slot;
420 if(proxy.impl->getter(var_slot) && var_slot)
421 {
422 return var_slot.assign(value);
423 }
424 return false;
425 };
426 return proxy;
427}
428
429auto make_property_proxy(const meta_any_proxy& var_proxy, const entt::meta_data& prop) -> meta_any_proxy
430{
431 meta_any_proxy prop_proxy;
432 prop_proxy.impl->parent = var_proxy.impl;
433 prop_proxy.impl->type_name = entt::get_pretty_name(prop.type());
434 prop_proxy.impl->name = [&]()
435 {
436 const auto& name = var_proxy.impl->name;
437 if(name.empty())
438 {
439 return entt::get_pretty_name(prop);
440 }
441 return fmt::format("{}/{}", name, entt::get_pretty_name(prop));
442 }();
443 prop_proxy.impl->getter = [parent_proxy = var_proxy, prop](entt::meta_any& result)
444 {
445 entt::meta_any var;
446 if(parent_proxy.impl->getter(var) && var)
447 {
448 result = prop.get(var);
449 return true;
450 }
451 return false;
452 };
453 prop_proxy.impl->setter = [parent_proxy = var_proxy, prop](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
454 {
455 entt::meta_any var;
456 if(parent_proxy.impl->getter(var) && var)
457 {
458 prop.set(var, value);
459 return parent_proxy.impl->setter(parent_proxy, var, execution_count);
460 }
461 return false;
462 };
463 return prop_proxy;
464}
465} // namespace unravel
Manages ImGui layout for property inspection in the editor.
Definition inspector.h:25
property_layout()
Default constructor that registers this layout in the global stack.
Definition inspector.cpp:68
auto push_tree_layout(ImGuiTreeNodeFlags flags=0, bool default_open=true) -> bool
Creates a collapsible tree node layout for nested properties.
void push_layout(bool auto_proceed_to_next_column=true)
Initializes ImGui layout with tables and property label.
static auto get_current() -> property_layout *
Gets the currently active property layout from the global stack.
Definition inspector.cpp:59
void set_data(const entt::meta_data &prop, const entt::meta_any &object, bool columns=true)
Updates layout data from meta property.
void pop_layout()
Cleans up ImGui state (IDs, tables, tree nodes)
void prepare_for_item()
Prepares ImGui for rendering the property value widget.
~property_layout()
Destructor that cleans up ImGui state and removes from stack.
float x
std::string name
Definition hub.cpp:33
const char * tooltip
void PushFont(Font::Enum _font)
Definition imgui.cpp:646
auto GetTintedStyleColor(ImGuiCol idx, float multiplier=1.0f) -> ImVec4
Definition inspector.cpp:14
void PopWindowFontScale()
Definition imgui.cpp:734
void PushWindowFontScale(float scale)
Definition imgui.cpp:721
auto get_property_hint(const entt::meta_any &object, const entt::meta_data &prop) -> std::string
auto get_pretty_name(const meta_type &t) -> std::string
auto get_attribute_as< std::string >(const meta_custom &custom, const char *name) -> std::string
Definition reflection.h:35
auto capitalize(const std::string &word) -> std::string
Definition utils.cpp:49
auto make_entity_proxy(entt::meta_any &var, const std::string &name) -> meta_any_proxy
Like make_proxy, but for entt::handle roots resolved by id_component UUID each access.
auto make_property_proxy(const meta_any_proxy &var_proxy, const entt::meta_data &prop) -> meta_any_proxy
auto make_proxy(entt::meta_any &var, const std::string &name) -> meta_any_proxy
Creates a simple proxy for direct variable access.
std::vector< math::color > color
static auto context() -> rtti::context &
Definition engine.cpp:111
std::unique_ptr< property_layout > layout_
Layout manager for this inspector's UI.
Definition inspector.h:363
virtual void after_inspect(const entt::meta_data &prop, const entt::meta_any &object)
Called after inspecting a property to clean up layout.
virtual void before_inspect(const entt::meta_data &prop, const entt::meta_any &object)
Called before inspecting a property to set up layout.
Safe deferred property access proxy for arbitrary object properties.
Definition inspector.h:216
std::shared_ptr< meta_any_proxy_impl > impl
Definition inspector.h:274
Global context for tracking prefab override changes during inspection.
Definition inspectors.h:99
property_layout_group(const std::string &name)
Definition inspector.cpp:42
gfx::uniform_handle handle
Definition uniform.cpp:9