NcEngine
ImGuiUtility.h
Go to the documentation of this file.
1
10#pragma once
11
12#include "ncengine/type/EngineTypes.h"
14
15#include "imgui.h"
16#include "ncmath/Color.h"
17#include "ncmath/Vector.h"
18#include "ncmath/Geometry.h"
19
20#include <algorithm>
21#include <functional>
22#include <numbers>
23#include <span>
24#include <string>
25#include <string_view>
26
28#define IMGUI_SCOPE_UNIQUE_NAME_HELPER(name, lineNumber) name ## lineNumber
29#define IMGUI_SCOPE_UNIQUE_NAME(name, lineNumber) IMGUI_SCOPE_UNIQUE_NAME_HELPER(name, lineNumber)
33#define IMGUI_SCOPE(Property, ...) auto IMGUI_SCOPE_UNIQUE_NAME(imguiProp, __LINE__) = Property(__VA_ARGS__)
34
35namespace nc::ui
36{
37constexpr auto g_defaultItemWidth = 50.0f;
38constexpr auto g_maxPos = 5000.0f;
39constexpr auto g_minPos = -g_maxPos;
40constexpr auto g_maxAngle = std::numbers::pi_v<float> * 2.0f;
41constexpr auto g_minAngle = -g_maxAngle;
42constexpr auto g_minScale = 0.1f;
43constexpr auto g_maxScale = 1000.0f;
44
46template<class F>
47void Window(const char* label, F&& drawContents);
48
50template<class F>
51void Window(const char* label, ImGuiWindowFlags flags, F&& drawContents);
52
54template<class F>
55void ChildWindow(const char* label, F&& drawContents);
56
58template<class F>
59void ChildWindow(const char* label, ImGuiChildFlags flags, F&& drawContents);
60
62auto IsWindowBackgroundClicked() -> bool;
63
65auto InputInt(int& value, const char* label, int step = 1) -> bool;
66
68auto InputU8(uint8_t& value, const char* label) -> bool;
69
71auto InputU16(uint16_t& value, const char* label) -> bool;
72
74auto InputU32(uint32_t& value, const char* label) -> bool;
75
77auto InputU64(uint64_t& value, const char* label) -> bool;
78
80auto DragFloat(float& value, const char* label, float speed = 1.0f, float min = 0.0f, float max = 1000.0f) -> bool;
81
83auto Checkbox(bool& value, const char* name) -> bool;
84
86auto InputVector3(Vector3& value, const char* label, float speed = 1.0f, float min = 0.0f, float max = 100.0f) -> bool;
87
89auto InputPosition(Vector3& value, const char* label) -> bool;
90
92auto InputAngles(Vector3& value, const char* label) -> bool;
93
95auto InputScale(Vector3& value, const char* label, float min = g_minScale, float max = g_maxScale) -> bool;
96
98auto InputAxis(Vector3& value, const char* label, float min = -1.0f, float max = 1.0f) -> bool;
99
101auto InputReferenceFrame(Vector3& basis, Vector3& normal, const char* basisLabel, const char* normalLabel) -> bool;
102
104auto InputColor3(Vector3& value, const char* label) -> bool;
105
107auto InputColor4(Vector4& value, const char* label) -> bool;
108
110auto InputGradient(Gradient& value, const char* lable) -> bool;
111
113auto Combobox(std::string& value, const char* label, std::span<const std::string_view> items);
114
116auto FilteredCombobox(std::string& value, const char* label, std::span<const std::string_view> items, auto&& disableIf);
117
119auto InputText(std::string& value, const char* label) -> bool;
120
122auto SelectableWidget(bool selected, const ImVec2& size, auto&& widget) -> bool;
123
125void SetTooltip(const char* text);
126
132template<class Getter, class Setter>
134{
135 Getter get;
136 Setter set;
137 const char* name = "";
138};
139
153auto PropertyWidget(const auto& property, auto& instance, auto&& widget, auto&&... args) -> bool;
154
156template<class T>
157void DragAndDropSource(T* item);
158
160template<class T, std::invocable<T*> F>
161void DragAndDropTarget(F&& func);
162
164void SameLineSpaced();
165
167void SameLineRightAligned(float itemWidth);
168
170void SeparatorSpaced();
171
174{
175 explicit ImGuiId(const char* id) { ImGui::PushID(id); }
176 explicit ImGuiId(int id) { ImGui::PushID(id); }
177 ~ImGuiId() noexcept { ImGui::PopID(); }
178};
179
181struct Indent
182{
183 explicit Indent() { ImGui::Indent(); }
184 ~Indent() noexcept { ImGui::Unindent(); }
185};
186
189{
190 explicit StyleColor(ImGuiCol index, ImU32 col) { ImGui::PushStyleColor(index, col); }
191 explicit StyleColor(ImGuiCol index, const ImVec4& col) { ImGui::PushStyleColor(index, col); }
192 explicit StyleColor(ImGuiCol index, const Vector4& col) { ImGui::PushStyleColor(index, col); }
193 ~StyleColor() noexcept { ImGui::PopStyleColor(); }
194};
195
198{
199 explicit StyleVar(ImGuiStyleVar index, const ImVec2& value) { ImGui::PushStyleVar(index, value); }
200 ~StyleVar() noexcept { ImGui::PopStyleVar(); }
201};
202
205{
206 explicit StyleFont(ImFont* font) { ImGui::PushFont(font); }
207 ~StyleFont() noexcept { ImGui::PopFont(); }
208};
209
212{
213 explicit ItemWidth(float itemWidth) { ImGui::PushItemWidth(itemWidth); }
214 ~ItemWidth() noexcept { ImGui::PopItemWidth(); }
215};
216
219{
220 explicit DisableIf(bool condition) { ImGui::BeginDisabled(condition); }
221 ~DisableIf() noexcept { ImGui::EndDisabled(); }
222};
223
226inline auto IsCapturingKeyboard() -> bool
227{
228 return ImGui::GetIO().WantCaptureKeyboard;
229}
230
233inline auto IsCapturingMouse() -> bool
234{
235 return ImGui::GetIO().WantCaptureMouse;
236}
237
238template<class F>
239void Window(const char* label, ImGuiWindowFlags flags, F&& drawContents)
240{
241 if (ImGui::Begin(label, nullptr, flags))
242 {
243 drawContents();
244 }
245
246 ImGui::End();
247}
248
249template<class F>
250void Window(const char* label, F&& drawContents)
251{
252 Window(label, ImGuiWindowFlags_None, std::forward<F>(drawContents));
253}
254
255template<class F>
256void ChildWindow(const char* label, ImGuiChildFlags flags, F&& drawContents)
257{
258 if (ImGui::BeginChild(label, {0, 0}, flags))
259 {
260 drawContents();
261 };
262
263 ImGui::EndChild();
264}
265
266template<class F>
267void ChildWindow(const char* label, F&& drawContents)
268{
269 if (ImGui::BeginChild(label, {0, 0}, true))
270 {
271 drawContents();
272 };
273
274 ImGui::EndChild();
275}
276
277inline auto IsWindowBackgroundClicked() -> bool
278{
279 return ImGui::IsWindowHovered() && ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered();
280}
281
282inline void SameLineSpaced()
283{
284 ImGui::SameLine();
285 ImGui::Spacing();
286 ImGui::SameLine();
287}
288
289inline void SameLineRightAligned(float itemWidth)
290{
291 const auto availableWidth = ImGui::GetContentRegionAvail().x;
292 itemWidth += + ImGui::GetStyle().FramePadding.x * 2.0f;
293 ImGui::SameLine(ImGui::GetCursorPosX() + availableWidth - itemWidth);
294}
295
296inline void SeparatorSpaced()
297{
298 ImGui::Spacing();
299 ImGui::Separator();
300 ImGui::Spacing();
301}
302
303inline auto InputInt(int& value, const char* label, int step) -> bool
304{
305 return ImGui::InputInt(label, &value, step);
306}
307
308inline auto InputU8(uint8_t& value, const char* label) -> bool
309{
310 constexpr uint8_t step = 1;
311 return ImGui::InputScalar(label, ImGuiDataType_U8, &value, &step);
312}
313
314inline auto InputU16(uint16_t& value, const char* label) -> bool
315{
316 constexpr uint16_t step = 1;
317 return ImGui::InputScalar(label, ImGuiDataType_U16, &value, &step);
318}
319
320inline auto InputU32(unsigned& value, const char* label) -> bool
321{
322 constexpr uint32_t step = 1;
323 return ImGui::InputScalar(label, ImGuiDataType_U32, &value, &step);
324}
325
326inline auto InputU64(uint64_t& value, const char* label) -> bool
327{
328 constexpr uint64_t step = 1;
329 return ImGui::InputScalar(label, ImGuiDataType_U64, &value, &step);
330}
331
332inline auto DragFloat(float& value, const char* label, float speed, float min, float max) -> bool
333{
334 return ImGui::DragFloat(label, &value, speed, min, max, "%.3f", ImGuiSliderFlags_AlwaysClamp);
335}
336
337inline auto Checkbox(bool& value, const char* name) -> bool
338{
339 return ImGui::Checkbox(name, &value);
340}
341
342inline auto InputVector3(Vector3& value, const char* label, float speed, float min, float max) -> bool
343{
344 return ImGui::DragFloat3(label, &value.x, speed, min, max, "%.3f", ImGuiSliderFlags_AlwaysClamp);
345}
346
347inline auto InputPosition(Vector3& value, const char* label) -> bool
348{
349 return ImGui::DragFloat3(label, &value.x, 1.0f, g_minPos, g_maxPos, "%.3f", ImGuiSliderFlags_AlwaysClamp);
350}
351
352inline auto InputAngles(Vector3& value, const char* label) -> bool
353{
354 return ImGui::DragFloat3(label, &value.x, 0.1f, g_minAngle, g_maxAngle, "%.3f", ImGuiSliderFlags_AlwaysClamp);
355}
356
357inline auto InputScale(Vector3& value, const char* label, float min, float max) -> bool
358{
359 return ImGui::DragFloat3(label, &value.x, 0.5f, min, max, "%.3f", ImGuiSliderFlags_AlwaysClamp);
360}
361
362inline auto InputAxis(Vector3& value, const char* label, float min, float max) -> bool
363{
364 const auto previous = value;
365 if (ImGui::DragFloat3(label, &value.x, 0.1f, min, max, "%.3f", ImGuiSliderFlags_AlwaysClamp))
366 {
367 // When a component is changed to a maximum, other values are still potentially non-zero. These cases need to
368 // be hard reset to the correct axis otherwise normalization prevents ever being able to reach it.
369 if (value.x != previous.x && std::fabs(value.x) == 1.0f)
370 value = Vector3{value.x, 0.0f, 0.0f};
371 else if (value.y != previous.y && std::fabs(value.y) == 1.0f)
372 value = Vector3{0.0f, value.y, 0.0f};
373 else if (value.z != previous.z && std::fabs(value.z) == 1.0f)
374 value = Vector3{0.0f, 0.0f, value.z};
375 else if (value == Vector3::Zero()) // prevent problems when 0 is directly enterered
376 value = previous;
377 else
378 value = Normalize(value);
379
380 return true;
381 }
382
383 return false;
384}
385
386inline auto InputReferenceFrame(Vector3& basis, Vector3& normal, const char* basisLabel, const char* normalLabel) -> bool
387{
388 auto modified = false;
389 if (InputAxis(basis, basisLabel))
390 {
391 normal = ClosestOrthogonal(normal, basis);
392 modified = true;
393 }
394
395 if (InputAxis(normal, normalLabel))
396 {
397 basis = ClosestOrthogonal(basis, normal);
398 modified = true;
399 }
400
401 return modified;
402}
403
404inline auto InputColor(Vector3& value, const char* label) -> bool
405{
406 return ImGui::ColorEdit3(label, &value.x, ImGuiColorEditFlags_NoInputs);
407}
408
409inline auto InputColor3(Vector3& value, const char* label) -> bool
410{
411 return ImGui::ColorEdit3(label, &value.x, ImGuiColorEditFlags_NoInputs);
412}
413
414inline auto InputColor4(Vector4& value, const char* label) -> bool
415{
416 return ImGui::ColorEdit4(label, &value.x, ImGuiColorEditFlags_NoInputs);
417}
418
419inline auto InputGradient(Gradient& value, const char* label) -> bool
420{
421 IMGUI_SCOPE(ui::ImGuiId, label);
422 const auto startColor = ImGui::ColorConvertFloat4ToU32(ImVec4{value.start});
423 const auto endColor = ImGui::ColorConvertFloat4ToU32(ImVec4{value.end});
424 const auto canvasPos = ImGui::GetCursorScreenPos();
425 const auto canvasSize = ImVec2{ImGui::CalcItemWidth(), ImGui::GetTextLineHeight()};
426 auto* drawList = ImGui::GetWindowDrawList();
427 drawList->AddRectFilledMultiColor(canvasPos, canvasPos + canvasSize, startColor, endColor, endColor, startColor);
428 ImGui::InvisibleButton("gradientbar", canvasSize);
429
430 if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
431 {
432 const auto mouseX = ImGui::GetIO().MousePos.x;
433 if (mouseX < canvasPos.x + canvasSize.x * 0.5f)
434 ImGui::OpenPopup("StartPicker");
435 else
436 ImGui::OpenPopup("EndPicker");
437 }
438
439 auto changed = false;
440 if (ImGui::BeginPopup("StartPicker"))
441 {
442 changed |= ImGui::ColorPicker4("Start Color", &value.start.x);
443 ImGui::EndPopup();
444 }
445
446 if (ImGui::BeginPopup("EndPicker"))
447 {
448 changed |= ImGui::ColorPicker4("End Color", &value.end.x);
449 ImGui::EndPopup();
450 }
451
452 ImGui::SameLine();
453 ImGui::TextUnformatted(label);
454 ImGui::Dummy(ImVec2{0.0f, 5.0f});
455 return changed;
456}
457
458inline auto Combobox(std::string& value, const char* label, std::span<const std::string_view> items)
459{
460 if (ImGui::BeginCombo(label, value.c_str()))
461 {
462 const auto selected = std::ranges::find_if(items, [](const auto& text)
463 {
464 return ImGui::Selectable(text.data());
465 });
466
467 ImGui::EndCombo();
468
469 if (selected != std::cend(items))
470 {
471 value = selected->data();
472 return true;
473 }
474 }
475
476 return false;
477}
478
479inline auto FilteredCombobox(std::string& value, const char* label, std::span<const std::string_view> items, auto&& disableIf)
480{
481 if (ImGui::BeginCombo(label, value.c_str()))
482 {
483 const auto selected = std::ranges::find_if(items, [&disableIf](const auto& text)
484 {
485 IMGUI_SCOPE(DisableIf, disableIf(text));
486 return ImGui::Selectable(text.data());
487 });
488
489 ImGui::EndCombo();
490
491 if (selected != std::cend(items))
492 {
493 value = selected->data();
494 return true;
495 }
496 }
497
498 return false;
499}
500
501namespace internal
502{
504{
505 std::string* string;
506};
507
508inline auto InputTextCallback(ImGuiInputTextCallbackData* data) -> int
509{
510 if(data->EventFlag == ImGuiInputTextFlags_CallbackResize)
511 {
512 InputTextCallbackUserData* userData = static_cast<InputTextCallbackUserData*>(data->UserData);
513 auto* str = userData->string;
514 str->resize(data->BufTextLen);
515 data->Buf = (char*)str->c_str();
516 }
517
518 return 0;
519}
520} // namespace internal
521
522inline auto InputText(std::string& text, const char* label) -> bool
523{
524 auto flags = ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_EnterReturnsTrue;
525 auto userData = internal::InputTextCallbackUserData{.string = &text};
526 return ImGui::InputText(label, text.data(), text.capacity() + 1, flags, internal::InputTextCallback, &userData);
527}
528
529auto SelectableWidget(bool selected, const ImVec2& size, auto&& widget) -> bool
530{
531 static constexpr auto flags = ImGuiSelectableFlags_AllowDoubleClick |
532 ImGuiSelectableFlags_AllowItemOverlap;
533 auto clicked = false;
534 if (ImGui::Selectable("##select", selected, flags, size))
535 clicked = true;
536
537 ImGui::SameLine();
538 widget();
539 if (ImGui::IsItemClicked(0))
540 {
541 clicked = true;
542 ImGui::SetKeyboardFocusHere(-1);
543 }
544
545 return clicked;
546};
547
548auto PropertyWidget(const auto& property, auto& instance, auto&& widget, auto&&... args) -> bool
549{
550 auto value = std::invoke(property.get, instance);
551 if (widget(value, property.name, std::forward<decltype(args)>(args)...))
552 {
553 std::invoke(property.set, instance, value);
554 return true;
555 }
556
557 return false;
558}
559
560template<class T>
561void DragAndDropSource(T* item)
562{
563 if(ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
564 {
565 ImGui::SetDragDropPayload(type::Type<T>::name, item, sizeof(T));
566 ImGui::Text(type::Type<T>::name);
567 ImGui::EndDragDropSource();
568 }
569}
570
571template<class T, std::invocable<T*> F>
572void DragAndDropTarget(F&& func)
573{
574 if(ImGui::BeginDragDropTarget())
575 {
576 if(const auto* payload = ImGui::AcceptDragDropPayload(type::Type<T>::name))
577 {
578 func(static_cast<std::add_pointer_t<T>>(payload->Data));
579 }
580
581 ImGui::EndDragDropTarget();
582 }
583}
584
585inline void SetTooltip(const char* text)
586{
587 if (ImGui::IsItemHovered())
588 {
589 ImGui::SetTooltip("%s", text);
590 }
591}
592} // namespace nc::ui
void SameLineRightAligned(float itemWidth)
UI layout helper for right aligning an item on the same line as the previous item.
Definition: ImGuiUtility.h:289
auto IsCapturingMouse() -> bool
Check if the UI is currently using mouse events.
Definition: ImGuiUtility.h:233
auto InputU32(uint32_t &value, const char *label) -> bool
Input field UI widget for uint32_t.
auto InputColor4(Vector4 &value, const char *label) -> bool
RGBA color picker UI widget.
Definition: ImGuiUtility.h:414
void SameLineSpaced()
UI layout helper for spacing.
Definition: ImGuiUtility.h:282
auto PropertyWidget(const auto &property, auto &instance, auto &&widget, auto &&... args) -> bool
Invoke a widget function with a Property and object instance.
Definition: ImGuiUtility.h:548
auto InputReferenceFrame(Vector3 &basis, Vector3 &normal, const char *basisLabel, const char *normalLabel) -> bool
Vector3 UI widget constrained for two orthonormal axis inputs.
Definition: ImGuiUtility.h:386
auto FilteredCombobox(std::string &value, const char *label, std::span< const std::string_view > items, auto &&disableIf)
Combobox UI widget that allows disabling selection of entries.
Definition: ImGuiUtility.h:479
void DragAndDropTarget(F &&func)
Introduce a drag and drop target that can accept a payload of type T*.
Definition: ImGuiUtility.h:572
auto InputPosition(Vector3 &value, const char *label) -> bool
Vector3 UI widget constrained for position inputs.
Definition: ImGuiUtility.h:347
auto Combobox(std::string &value, const char *label, std::span< const std::string_view > items)
Combobox UI widget.
Definition: ImGuiUtility.h:458
void ChildWindow(const char *label, F &&drawContents)
Create a child window.
Definition: ImGuiUtility.h:267
void Window(const char *label, F &&drawContents)
Create a top-level window.
Definition: ImGuiUtility.h:250
auto InputText(std::string &value, const char *label) -> bool
Text input UI widget.
Definition: ImGuiUtility.h:522
auto InputScale(Vector3 &value, const char *label, float min=g_minScale, float max=g_maxScale) -> bool
Vector3 UI widget constrained for scale inputs.
Definition: ImGuiUtility.h:357
auto InputU16(uint16_t &value, const char *label) -> bool
Input field UI widget for uint16_t.
Definition: ImGuiUtility.h:314
auto InputU64(uint64_t &value, const char *label) -> bool
Input field UI widget for uint64_t.
Definition: ImGuiUtility.h:326
void SeparatorSpaced()
UI layout helper for separator.
Definition: ImGuiUtility.h:296
void DragAndDropSource(T *item)
Introduce a drag and drop source with item as its payload.
Definition: ImGuiUtility.h:561
auto IsWindowBackgroundClicked() -> bool
Check if the current window background is clicked on.
Definition: ImGuiUtility.h:277
auto DragFloat(float &value, const char *label, float speed=1.0f, float min=0.0f, float max=1000.0f) -> bool
Drag slider UI widget for float.
Definition: ImGuiUtility.h:332
void SetTooltip(const char *text)
Set tooltip text if the last item is hovered.
Definition: ImGuiUtility.h:585
auto InputGradient(Gradient &value, const char *lable) -> bool
RGBA color pickers over a two color gradient.
Definition: ImGuiUtility.h:419
auto SelectableWidget(bool selected, const ImVec2 &size, auto &&widget) -> bool
Wrap a widget with a selectable - allows for things like list box of input widgets.
Definition: ImGuiUtility.h:529
auto InputInt(int &value, const char *label, int step=1) -> bool
Input field UI widget for int.
Definition: ImGuiUtility.h:303
#define IMGUI_SCOPE(Property,...)
Create a variable to hold a scoped property.
Definition: ImGuiUtility.h:33
auto IsCapturingKeyboard() -> bool
Check if the UI is currently using keyboard events.
Definition: ImGuiUtility.h:226
auto InputU8(uint8_t &value, const char *label) -> bool
Input field UI widget for uint8_t.
Definition: ImGuiUtility.h:308
auto InputAngles(Vector3 &value, const char *label) -> bool
Vector3 UI widget constrained for angular (radians) inputs.
Definition: ImGuiUtility.h:352
auto InputAxis(Vector3 &value, const char *label, float min=-1.0f, float max=1.0f) -> bool
Vector3 UI widget constrained for normalized axis inputs.
Definition: ImGuiUtility.h:362
auto InputColor3(Vector3 &value, const char *label) -> bool
RGB color picker UI widget.
Definition: ImGuiUtility.h:409
auto Checkbox(bool &value, const char *name) -> bool
Checkbox UI widget for bool.
Definition: ImGuiUtility.h:337
auto InputVector3(Vector3 &value, const char *label, float speed=1.0f, float min=0.0f, float max=100.0f) -> bool
Vector3 UI widget with three float drag sliders.
Definition: ImGuiUtility.h:342
auto Normalize(const Quaternion &quat) -> Quaternion
Return a normalized quaternion.
Gradient between two Colors.
Definition: Color.h:45
A three component vector.
Definition: Vector.h:29
A four component vector.
Definition: Vector.h:48
Definition: Type.h:11
RAII wrapper for conditionally disabling widgets within a scope.
Definition: ImGuiUtility.h:219
RAII wrapper for scoped uids.
Definition: ImGuiUtility.h:174
RAII wrapper for scoped indentation.
Definition: ImGuiUtility.h:182
RAII wrapper for scoped item width.
Definition: ImGuiUtility.h:212
Simple getter/setter-based property wrapper.
Definition: ImGuiUtility.h:134
RAII wrapper for scoped style color.
Definition: ImGuiUtility.h:189
RAII wrapper for ui font.
Definition: ImGuiUtility.h:205
RAII wrapper for scoped style var.
Definition: ImGuiUtility.h:198
Definition: ImGuiUtility.h:504