| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1 |
// dear imgui, v1.91.4 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2 |
// (widgets code) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4 |
/* |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6 |
Index of this file: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8 |
// [SECTION] Forward Declarations |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9 |
// [SECTION] Widgets: Text, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10 |
// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
11 |
// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
12 |
// [SECTION] Widgets: ComboBox |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
13 |
// [SECTION] Data Type and Data Formatting Helpers |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
14 |
// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
15 |
// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
16 |
// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
17 |
// [SECTION] Widgets: InputText, InputTextMultiline |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
18 |
// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
19 |
// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
20 |
// [SECTION] Widgets: Selectable |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
21 |
// [SECTION] Widgets: Typing-Select support |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
22 |
// [SECTION] Widgets: Box-Select support |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
23 |
// [SECTION] Widgets: Multi-Select support |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
24 |
// [SECTION] Widgets: Multi-Select helpers |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
25 |
// [SECTION] Widgets: ListBox |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
26 |
// [SECTION] Widgets: PlotLines, PlotHistogram |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
27 |
// [SECTION] Widgets: Value helpers |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
28 |
// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
29 |
// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
30 |
// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
31 |
// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
32 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
33 |
*/ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
34 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
35 |
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
36 |
#define _CRT_SECURE_NO_WARNINGS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
37 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
38 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
39 |
#ifndef IMGUI_DEFINE_MATH_OPERATORS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
40 |
#define IMGUI_DEFINE_MATH_OPERATORS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
41 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
42 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
43 |
#include "imgui.h" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
44 |
#ifndef IMGUI_DISABLE |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
45 |
#include "imgui_internal.h" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
46 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
47 |
// System includes |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
48 |
#include <stdint.h> // intptr_t |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
49 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
50 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
51 |
// Warnings |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
52 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
53 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
54 |
// Visual Studio warnings |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
55 |
#ifdef _MSC_VER |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
56 |
#pragma warning (disable: 4127) // condition expression is constant |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
57 |
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
58 |
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
59 |
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
60 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
61 |
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
62 |
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
63 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
64 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
65 |
// Clang/GCC warnings with -Weverything |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
66 |
#if defined(__clang__) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
67 |
#if __has_warning("-Wunknown-warning-option") |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
68 |
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
69 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
70 |
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
71 |
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
72 |
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
73 |
#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
74 |
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
75 |
#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
76 |
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
77 |
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
78 |
#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
79 |
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
80 |
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
81 |
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
82 |
#elif defined(__GNUC__) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
83 |
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
84 |
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
85 |
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
86 |
#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
87 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
88 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
89 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
90 |
// Data |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
91 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
92 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
93 |
// Widgets |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
94 |
static const float DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f; // Time for drag-hold to activate items accepting the ImGuiButtonFlags_PressedOnDragDropHold button behavior. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
95 |
static const float DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f; // Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
96 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
97 |
// Those MIN/MAX values are not define because we need to point to them |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
98 |
static const signed char IM_S8_MIN = -128; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
99 |
static const signed char IM_S8_MAX = 127; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
100 |
static const unsigned char IM_U8_MIN = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
101 |
static const unsigned char IM_U8_MAX = 0xFF; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
102 |
static const signed short IM_S16_MIN = -32768; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
103 |
static const signed short IM_S16_MAX = 32767; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
104 |
static const unsigned short IM_U16_MIN = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
105 |
static const unsigned short IM_U16_MAX = 0xFFFF; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
106 |
static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
107 |
static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
108 |
static const ImU32 IM_U32_MIN = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
109 |
static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
110 |
#ifdef LLONG_MIN |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
111 |
static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
112 |
static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
113 |
#else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
114 |
static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
115 |
static const ImS64 IM_S64_MAX = 9223372036854775807LL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
116 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
117 |
static const ImU64 IM_U64_MIN = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
118 |
#ifdef ULLONG_MAX |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
119 |
static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
120 |
#else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
121 |
static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
122 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
123 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
124 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
125 |
// [SECTION] Forward Declarations |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
126 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
127 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
128 |
// For InputTextEx() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
129 |
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
130 |
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
131 |
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
132 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
133 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
134 |
// [SECTION] Widgets: Text, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
135 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
136 |
// - TextEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
137 |
// - TextUnformatted() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
138 |
// - Text() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
139 |
// - TextV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
140 |
// - TextColored() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
141 |
// - TextColoredV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
142 |
// - TextDisabled() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
143 |
// - TextDisabledV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
144 |
// - TextWrapped() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
145 |
// - TextWrappedV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
146 |
// - LabelText() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
147 |
// - LabelTextV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
148 |
// - BulletText() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
149 |
// - BulletTextV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
150 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
151 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
152 |
void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
153 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
154 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
155 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
156 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
157 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
158 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
159 |
// Accept null ranges |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
160 |
if (text == text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
161 |
text = text_end = ""; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
162 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
163 |
// Calculate length |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
164 |
const char* text_begin = text; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
165 |
if (text_end == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
166 |
text_end = text + strlen(text); // FIXME-OPT |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
167 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
168 |
const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
169 |
const float wrap_pos_x = window->DC.TextWrapPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
170 |
const bool wrap_enabled = (wrap_pos_x >= 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
171 |
if (text_end - text <= 2000 || wrap_enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
172 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
173 |
// Common case |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
174 |
const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
175 |
const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
176 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
177 |
ImRect bb(text_pos, text_pos + text_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
178 |
ItemSize(text_size, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
179 |
if (!ItemAdd(bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
180 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
181 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
182 |
// Render (we don't hide text after ## in this end-user function) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
183 |
RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
184 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
185 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
186 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
187 |
// Long text! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
188 |
// Perform manual coarse clipping to optimize for long multi-line text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
189 |
// - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
190 |
// - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
191 |
// - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
192 |
const char* line = text; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
193 |
const float line_height = GetTextLineHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
194 |
ImVec2 text_size(0, 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
195 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
196 |
// Lines to skip (can't skip when logging text) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
197 |
ImVec2 pos = text_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
198 |
if (!g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
199 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
200 |
int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
201 |
if (lines_skippable > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
202 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
203 |
int lines_skipped = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
204 |
while (line < text_end && lines_skipped < lines_skippable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
205 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
206 |
const char* line_end = (const char*)memchr(line, '\n', text_end - line); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
207 |
if (!line_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
208 |
line_end = text_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
209 |
if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
210 |
text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
211 |
line = line_end + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
212 |
lines_skipped++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
213 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
214 |
pos.y += lines_skipped * line_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
215 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
216 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
217 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
218 |
// Lines to render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
219 |
if (line < text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
220 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
221 |
ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
222 |
while (line < text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
223 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
224 |
if (IsClippedEx(line_rect, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
225 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
226 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
227 |
const char* line_end = (const char*)memchr(line, '\n', text_end - line); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
228 |
if (!line_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
229 |
line_end = text_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
230 |
text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
231 |
RenderText(pos, line, line_end, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
232 |
line = line_end + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
233 |
line_rect.Min.y += line_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
234 |
line_rect.Max.y += line_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
235 |
pos.y += line_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
236 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
237 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
238 |
// Count remaining lines |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
239 |
int lines_skipped = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
240 |
while (line < text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
241 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
242 |
const char* line_end = (const char*)memchr(line, '\n', text_end - line); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
243 |
if (!line_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
244 |
line_end = text_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
245 |
if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
246 |
text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
247 |
line = line_end + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
248 |
lines_skipped++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
249 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
250 |
pos.y += lines_skipped * line_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
251 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
252 |
text_size.y = (pos - text_pos).y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
253 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
254 |
ImRect bb(text_pos, text_pos + text_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
255 |
ItemSize(text_size, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
256 |
ItemAdd(bb, 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
257 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
258 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
259 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
260 |
void ImGui::TextUnformatted(const char* text, const char* text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
261 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
262 |
TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
263 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
264 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
265 |
void ImGui::Text(const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
266 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
267 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
268 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
269 |
TextV(fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
270 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
271 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
272 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
273 |
void ImGui::TextV(const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
274 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
275 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
276 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
277 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
278 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
279 |
const char* text, *text_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
280 |
ImFormatStringToTempBufferV(&text, &text_end, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
281 |
TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
282 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
283 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
284 |
void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
285 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
286 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
287 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
288 |
TextColoredV(col, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
289 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
290 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
291 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
292 |
void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
293 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
294 |
PushStyleColor(ImGuiCol_Text, col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
295 |
TextV(fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
296 |
PopStyleColor(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
297 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
298 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
299 |
void ImGui::TextDisabled(const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
300 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
301 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
302 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
303 |
TextDisabledV(fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
304 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
305 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
306 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
307 |
void ImGui::TextDisabledV(const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
308 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
309 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
310 |
PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
311 |
TextV(fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
312 |
PopStyleColor(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
313 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
314 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
315 |
void ImGui::TextWrapped(const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
316 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
317 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
318 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
319 |
TextWrappedV(fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
320 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
321 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
322 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
323 |
void ImGui::TextWrappedV(const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
324 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
325 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
326 |
const bool need_backup = (g.CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
327 |
if (need_backup) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
328 |
PushTextWrapPos(0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
329 |
TextV(fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
330 |
if (need_backup) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
331 |
PopTextWrapPos(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
332 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
333 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
334 |
void ImGui::LabelText(const char* label, const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
335 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
336 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
337 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
338 |
LabelTextV(label, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
339 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
340 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
341 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
342 |
// Add a label+text combo aligned to other label+value widgets |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
343 |
void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
344 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
345 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
346 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
347 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
348 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
349 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
350 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
351 |
const float w = CalcItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
352 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
353 |
const char* value_text_begin, *value_text_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
354 |
ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
355 |
const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
356 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
357 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
358 |
const ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
359 |
const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
360 |
const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
361 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
362 |
if (!ItemAdd(total_bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
363 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
364 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
365 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
366 |
RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
367 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
368 |
RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
369 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
370 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
371 |
void ImGui::BulletText(const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
372 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
373 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
374 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
375 |
BulletTextV(fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
376 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
377 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
378 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
379 |
// Text with a little bullet aligned to the typical tree node. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
380 |
void ImGui::BulletTextV(const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
381 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
382 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
383 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
384 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
385 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
386 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
387 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
388 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
389 |
const char* text_begin, *text_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
390 |
ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
391 |
const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
392 |
const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
393 |
ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
394 |
pos.y += window->DC.CurrLineTextBaseOffset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
395 |
ItemSize(total_size, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
396 |
const ImRect bb(pos, pos + total_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
397 |
if (!ItemAdd(bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
398 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
399 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
400 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
401 |
ImU32 text_col = GetColorU32(ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
402 |
RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
403 |
RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
404 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
405 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
406 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
407 |
// [SECTION] Widgets: Main |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
408 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
409 |
// - ButtonBehavior() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
410 |
// - Button() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
411 |
// - SmallButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
412 |
// - InvisibleButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
413 |
// - ArrowButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
414 |
// - CloseButton() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
415 |
// - CollapseButton() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
416 |
// - GetWindowScrollbarID() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
417 |
// - GetWindowScrollbarRect() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
418 |
// - Scrollbar() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
419 |
// - ScrollbarEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
420 |
// - Image() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
421 |
// - ImageButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
422 |
// - Checkbox() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
423 |
// - CheckboxFlagsT() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
424 |
// - CheckboxFlags() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
425 |
// - RadioButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
426 |
// - ProgressBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
427 |
// - Bullet() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
428 |
// - Hyperlink() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
429 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
430 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
431 |
// The ButtonBehavior() function is key to many interactions and used by many/most widgets. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
432 |
// Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_), |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
433 |
// this code is a little complex. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
434 |
// By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
435 |
// See the series of events below and the corresponding state reported by dear imgui: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
436 |
//------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
437 |
// with PressedOnClickRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
438 |
// Frame N+0 (mouse is outside bb) - - - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
439 |
// Frame N+1 (mouse moves inside bb) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
440 |
// Frame N+2 (mouse button is down) - true true true - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
441 |
// Frame N+3 (mouse button is down) - true true - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
442 |
// Frame N+4 (mouse moves outside bb) - - true - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
443 |
// Frame N+5 (mouse moves inside bb) - true true - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
444 |
// Frame N+6 (mouse button is released) true true - - true - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
445 |
// Frame N+7 (mouse button is released) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
446 |
// Frame N+8 (mouse moves outside bb) - - - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
447 |
//------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
448 |
// with PressedOnClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
449 |
// Frame N+2 (mouse button is down) true true true true - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
450 |
// Frame N+3 (mouse button is down) - true true - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
451 |
// Frame N+6 (mouse button is released) - true - - true - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
452 |
// Frame N+7 (mouse button is released) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
453 |
//------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
454 |
// with PressedOnRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
455 |
// Frame N+2 (mouse button is down) - true - - - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
456 |
// Frame N+3 (mouse button is down) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
457 |
// Frame N+6 (mouse button is released) true true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
458 |
// Frame N+7 (mouse button is released) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
459 |
//------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
460 |
// with PressedOnDoubleClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
461 |
// Frame N+0 (mouse button is down) - true - - - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
462 |
// Frame N+1 (mouse button is down) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
463 |
// Frame N+2 (mouse button is released) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
464 |
// Frame N+3 (mouse button is released) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
465 |
// Frame N+4 (mouse button is down) true true true true - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
466 |
// Frame N+5 (mouse button is down) - true true - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
467 |
// Frame N+6 (mouse button is released) - true - - true - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
468 |
// Frame N+7 (mouse button is released) - true - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
469 |
//------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
470 |
// Note that some combinations are supported, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
471 |
// - PressedOnDragDropHold can generally be associated with any flag. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
472 |
// - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
473 |
//------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
474 |
// The behavior of the return-value changes when ImGuiButtonFlags_Repeat is set: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
475 |
// Repeat+ Repeat+ Repeat+ Repeat+ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
476 |
// PressedOnClickRelease PressedOnClick PressedOnRelease PressedOnDoubleClick |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
477 |
//------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
478 |
// Frame N+0 (mouse button is down) - true - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
479 |
// ... - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
480 |
// Frame N + RepeatDelay true true - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
481 |
// ... - - - - |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
482 |
// Frame N + RepeatDelay + RepeatRate*N true true - true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
483 |
//------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
484 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
485 |
// - FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
486 |
// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);' |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
487 |
// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
488 |
// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
489 |
// One idiom which was previously valid which will now emit a warning is when using multiple overlayed ButtonBehavior() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
490 |
// with same ID and different MouseButton (see #8030). You can fix it by: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
491 |
// (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
492 |
// or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
493 |
bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
494 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
495 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
496 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
497 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
498 |
// Default only reacts to left mouse button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
499 |
if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
500 |
flags |= ImGuiButtonFlags_MouseButtonLeft; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
501 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
502 |
// Default behavior requires click + release inside bounding box |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
503 |
if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
504 |
flags |= ImGuiButtonFlags_PressedOnDefault_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
505 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
506 |
// Default behavior inherited from item flags |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
507 |
// Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
508 |
ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
509 |
if (flags & ImGuiButtonFlags_AllowOverlap) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
510 |
item_flags |= ImGuiItemFlags_AllowOverlap; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
511 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
512 |
ImGuiWindow* backup_hovered_window = g.HoveredWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
513 |
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
514 |
if (flatten_hovered_children) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
515 |
g.HoveredWindow = window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
516 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
517 |
#ifdef IMGUI_ENABLE_TEST_ENGINE |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
518 |
// Alternate registration spot, for when caller didn't use ItemAdd() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
519 |
if (g.LastItemData.ID != id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
520 |
IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
521 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
522 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
523 |
bool pressed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
524 |
bool hovered = ItemHoverable(bb, id, item_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
525 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
526 |
// Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
527 |
if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
528 |
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
529 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
530 |
hovered = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
531 |
SetHoveredID(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
532 |
if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
533 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
534 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
535 |
g.DragDropHoldJustPressedId = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
536 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
537 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
538 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
539 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
540 |
if (flatten_hovered_children) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
541 |
g.HoveredWindow = backup_hovered_window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
542 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
543 |
// Mouse handling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
544 |
const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
545 |
if (hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
546 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
547 |
IM_ASSERT(id != 0); // Lazily check inside rare path. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
548 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
549 |
// Poll mouse buttons |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
550 |
// - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
551 |
// - Technically we only need some values in one code path, but since this is gated by hovered test this is fine. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
552 |
int mouse_button_clicked = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
553 |
int mouse_button_released = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
554 |
for (int button = 0; button < 3; button++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
555 |
if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
556 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
557 |
if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
558 |
if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
559 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
560 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
561 |
// Process initial action |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
562 |
const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
563 |
if (mods_ok) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
564 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
565 |
if (mouse_button_clicked != -1 && g.ActiveId != id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
566 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
567 |
if (!(flags & ImGuiButtonFlags_NoSetKeyOwner)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
568 |
SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
569 |
if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
570 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
571 |
SetActiveID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
572 |
g.ActiveIdMouseButton = mouse_button_clicked; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
573 |
if (!(flags & ImGuiButtonFlags_NoNavFocus)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
574 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
575 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
576 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
577 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
578 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
579 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
580 |
FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
581 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
582 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
583 |
if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
584 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
585 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
586 |
if (flags & ImGuiButtonFlags_NoHoldingActiveId) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
587 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
588 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
589 |
SetActiveID(id, window); // Hold on ID |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
590 |
g.ActiveIdMouseButton = mouse_button_clicked; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
591 |
if (!(flags & ImGuiButtonFlags_NoNavFocus)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
592 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
593 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
594 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
595 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
596 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
597 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
598 |
FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
599 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
600 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
601 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
602 |
if (flags & ImGuiButtonFlags_PressedOnRelease) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
603 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
604 |
if (mouse_button_released != -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
605 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
606 |
const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
607 |
if (!has_repeated_at_least_once) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
608 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
609 |
if (!(flags & ImGuiButtonFlags_NoNavFocus)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
610 |
SetFocusID(id, window); // FIXME: Lack of FocusWindow() call here is inconsistent with other paths. Research why. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
611 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
612 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
613 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
614 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
615 |
// 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
616 |
// Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
617 |
if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
618 |
if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
619 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
620 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
621 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
622 |
if (pressed && g.IO.ConfigNavCursorVisibleAuto) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
623 |
g.NavCursorVisible = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
624 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
625 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
626 |
// Keyboard/Gamepad navigation handling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
627 |
// We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
628 |
if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
629 |
if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
630 |
hovered = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
631 |
if (g.NavActivateDownId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
632 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
633 |
bool nav_activated_by_code = (g.NavActivateId == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
634 |
bool nav_activated_by_inputs = (g.NavActivatePressedId == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
635 |
if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
636 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
637 |
// Avoid pressing multiple keys from triggering excessive amount of repeat events |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
638 |
const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
639 |
const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
640 |
const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
641 |
const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
642 |
nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
643 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
644 |
if (nav_activated_by_code || nav_activated_by_inputs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
645 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
646 |
// Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
647 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
648 |
SetActiveID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
649 |
g.ActiveIdSource = g.NavInputSource; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
650 |
if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
651 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
652 |
if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
653 |
g.ActiveIdFromShortcut = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
654 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
655 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
656 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
657 |
// Process while held |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
658 |
bool held = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
659 |
if (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
660 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
661 |
if (g.ActiveIdSource == ImGuiInputSource_Mouse) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
662 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
663 |
if (g.ActiveIdIsJustActivated) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
664 |
g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
665 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
666 |
const int mouse_button = g.ActiveIdMouseButton; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
667 |
if (mouse_button == -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
668 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
669 |
// Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
670 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
671 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
672 |
else if (IsMouseDown(mouse_button, test_owner_id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
673 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
674 |
held = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
675 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
676 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
677 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
678 |
bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
679 |
bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
680 |
if ((release_in || release_anywhere) && !g.DragDropActive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
681 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
682 |
// Report as pressed when releasing the mouse (this is the most common path) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
683 |
bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
684 |
bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
685 |
bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
686 |
if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
687 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
688 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
689 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
690 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
691 |
if (!(flags & ImGuiButtonFlags_NoNavFocus) && g.IO.ConfigNavCursorVisibleAuto) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
692 |
g.NavCursorVisible = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
693 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
694 |
else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
695 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
696 |
// When activated using Nav, we hold on the ActiveID until activation button is released |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
697 |
if (g.NavActivateDownId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
698 |
held = true; // hovered == true not true as we are already likely hovered on direct activation. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
699 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
700 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
701 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
702 |
if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
703 |
g.ActiveIdHasBeenPressedBefore = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
704 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
705 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
706 |
// Activation highlight (this may be a remote activation) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
707 |
if (g.NavHighlightActivatedId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
708 |
hovered = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
709 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
710 |
if (out_hovered) *out_hovered = hovered; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
711 |
if (out_held) *out_held = held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
712 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
713 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
714 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
715 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
716 |
bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
717 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
718 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
719 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
720 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
721 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
722 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
723 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
724 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
725 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
726 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
727 |
ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
728 |
if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
729 |
pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
730 |
ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
731 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
732 |
const ImRect bb(pos, pos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
733 |
ItemSize(size, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
734 |
if (!ItemAdd(bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
735 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
736 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
737 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
738 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
739 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
740 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
741 |
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
742 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
743 |
RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
744 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
745 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
746 |
LogSetNextTextDecoration("[", "]"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
747 |
RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
748 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
749 |
// Automatically close popups |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
750 |
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
751 |
// CloseCurrentPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
752 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
753 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
754 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
755 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
756 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
757 |
bool ImGui::Button(const char* label, const ImVec2& size_arg) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
758 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
759 |
return ButtonEx(label, size_arg, ImGuiButtonFlags_None); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
760 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
761 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
762 |
// Small buttons fits within text without additional vertical spacing. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
763 |
bool ImGui::SmallButton(const char* label) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
764 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
765 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
766 |
float backup_padding_y = g.Style.FramePadding.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
767 |
g.Style.FramePadding.y = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
768 |
bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
769 |
g.Style.FramePadding.y = backup_padding_y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
770 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
771 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
772 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
773 |
// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
774 |
// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
775 |
bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
776 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
777 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
778 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
779 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
780 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
781 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
782 |
// Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
783 |
IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
784 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
785 |
const ImGuiID id = window->GetID(str_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
786 |
ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
787 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
788 |
ItemSize(size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
789 |
if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
790 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
791 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
792 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
793 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
794 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
795 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
796 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
797 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
798 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
799 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
800 |
bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
801 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
802 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
803 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
804 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
805 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
806 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
807 |
const ImGuiID id = window->GetID(str_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
808 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
809 |
const float default_size = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
810 |
ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
811 |
if (!ItemAdd(bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
812 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
813 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
814 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
815 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
816 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
817 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
818 |
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
819 |
const ImU32 text_col = GetColorU32(ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
820 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
821 |
RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
822 |
RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
823 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
824 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
825 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
826 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
827 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
828 |
bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
829 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
830 |
float sz = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
831 |
return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
832 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
833 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
834 |
// Button to close a window |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
835 |
bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
836 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
837 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
838 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
839 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
840 |
// Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
841 |
// This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
842 |
const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
843 |
ImRect bb_interact = bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
844 |
const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
845 |
if (area_to_visible_ratio < 1.5f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
846 |
bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
847 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
848 |
// Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
849 |
// (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
850 |
bool is_clipped = !ItemAdd(bb_interact, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
851 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
852 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
853 |
bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
854 |
if (is_clipped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
855 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
856 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
857 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
858 |
ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
859 |
if (hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
860 |
window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
861 |
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
862 |
ImU32 cross_col = GetColorU32(ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
863 |
ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
864 |
float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
865 |
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
866 |
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
867 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
868 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
869 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
870 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
871 |
// The Collapse button also functions as a Dock Menu button. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
872 |
bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
873 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
874 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
875 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
876 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
877 |
ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
878 |
bool is_clipped = !ItemAdd(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
879 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
880 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
881 |
if (is_clipped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
882 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
883 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
884 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
885 |
//bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
886 |
ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
887 |
ImU32 text_col = GetColorU32(ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
888 |
if (hovered || held) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
889 |
window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
890 |
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
891 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
892 |
if (dock_node) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
893 |
RenderArrowDockMenu(window->DrawList, bb.Min, g.FontSize, text_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
894 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
895 |
RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
896 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
897 |
// Switch to moving the window after mouse is moved beyond the initial drag threshold |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
898 |
if (IsItemActive() && IsMouseDragging(0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
899 |
StartMouseMovingWindowOrNode(window, dock_node, true); // Undock from window/collapse menu button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
900 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
901 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
902 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
903 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
904 |
ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
905 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
906 |
return window->GetID(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
907 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
908 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
909 |
// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
910 |
ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
911 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
912 |
const ImRect outer_rect = window->Rect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
913 |
const ImRect inner_rect = window->InnerRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
914 |
const float border_size = window->WindowBorderSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
915 |
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
916 |
IM_ASSERT(scrollbar_size > 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
917 |
if (axis == ImGuiAxis_X) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
918 |
return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
919 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
920 |
return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
921 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
922 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
923 |
void ImGui::Scrollbar(ImGuiAxis axis) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
924 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
925 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
926 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
927 |
const ImGuiID id = GetWindowScrollbarID(window, axis); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
928 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
929 |
// Calculate scrollbar bounding box |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
930 |
ImRect bb = GetWindowScrollbarRect(window, axis); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
931 |
ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
932 |
if (axis == ImGuiAxis_X) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
933 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
934 |
rounding_corners |= ImDrawFlags_RoundCornersBottomLeft; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
935 |
if (!window->ScrollbarY) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
936 |
rounding_corners |= ImDrawFlags_RoundCornersBottomRight; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
937 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
938 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
939 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
940 |
if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
941 |
rounding_corners |= ImDrawFlags_RoundCornersTopRight; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
942 |
if (!window->ScrollbarX) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
943 |
rounding_corners |= ImDrawFlags_RoundCornersBottomRight; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
944 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
945 |
float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
946 |
float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
947 |
ImS64 scroll = (ImS64)window->Scroll[axis]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
948 |
ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_visible, (ImS64)size_contents, rounding_corners); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
949 |
window->Scroll[axis] = (float)scroll; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
950 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
951 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
952 |
// Vertical/Horizontal scrollbar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
953 |
// The entire piece of code below is rather confusing because: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
954 |
// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
955 |
// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
956 |
// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
957 |
// Still, the code should probably be made simpler.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
958 |
bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
959 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
960 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
961 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
962 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
963 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
964 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
965 |
const float bb_frame_width = bb_frame.GetWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
966 |
const float bb_frame_height = bb_frame.GetHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
967 |
if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
968 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
969 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
970 |
// When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
971 |
float alpha = 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
972 |
if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
973 |
alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
974 |
if (alpha <= 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
975 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
976 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
977 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
978 |
const bool allow_interaction = (alpha >= 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
979 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
980 |
ImRect bb = bb_frame; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
981 |
bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
982 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
983 |
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
984 |
const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
985 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
986 |
// Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
987 |
// But we maintain a minimum size in pixel to allow for the user to still aim inside. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
988 |
IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
989 |
const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
990 |
const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
991 |
const float grab_h_norm = grab_h_pixels / scrollbar_size_v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
992 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
993 |
// Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
994 |
bool held = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
995 |
bool hovered = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
996 |
ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
997 |
ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
998 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
999 |
const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_visible_v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1000 |
float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1001 |
float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1002 |
if (held && allow_interaction && grab_h_norm < 1.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1003 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1004 |
const float scrollbar_pos_v = bb.Min[axis]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1005 |
const float mouse_pos_v = g.IO.MousePos[axis]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1006 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1007 |
// Click position in scrollbar normalized space (0.0f->1.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1008 |
const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1009 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1010 |
const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1011 |
if (g.ActiveIdIsJustActivated) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1012 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1013 |
// On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1014 |
const bool scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false || g.IO.KeyShift || held_dir == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1015 |
g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1016 |
g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1017 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1018 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1019 |
// Apply scroll (p_scroll_v will generally point on one member of window->Scroll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1020 |
// It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1021 |
if (g.ScrollbarSeekMode == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1022 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1023 |
// Absolute seeking |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1024 |
const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1025 |
*p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1026 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1027 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1028 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1029 |
// Page by page |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1030 |
if (IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_Repeat) && held_dir == g.ScrollbarSeekMode) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1031 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1032 |
float page_dir = (g.ScrollbarSeekMode > 0.0f) ? +1.0f : -1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1033 |
*p_scroll_v = ImClamp(*p_scroll_v + (ImS64)(page_dir * size_visible_v), (ImS64)0, scroll_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1034 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1035 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1036 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1037 |
// Update values for rendering |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1038 |
scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1039 |
grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1040 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1041 |
// Update distance to grab now that we have seek'ed and saturated |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1042 |
//if (seek_absolute) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1043 |
// g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1044 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1045 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1046 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1047 |
const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1048 |
const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1049 |
window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1050 |
ImRect grab_rect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1051 |
if (axis == ImGuiAxis_X) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1052 |
grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1053 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1054 |
grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1055 |
window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1056 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1057 |
return held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1058 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1059 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1060 |
// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1061 |
// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1062 |
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1063 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1064 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1065 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1066 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1067 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1068 |
const float border_size = (border_col.w > 0.0f) ? 1.0f : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1069 |
const ImVec2 padding(border_size, border_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1070 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1071 |
ItemSize(bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1072 |
if (!ItemAdd(bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1073 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1074 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1075 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1076 |
if (border_size > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1077 |
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f, ImDrawFlags_None, border_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1078 |
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1079 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1080 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1081 |
// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1082 |
// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1083 |
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1084 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1085 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1086 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1087 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1088 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1089 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1090 |
const ImVec2 padding = g.Style.FramePadding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1091 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1092 |
ItemSize(bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1093 |
if (!ItemAdd(bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1094 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1095 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1096 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1097 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1098 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1099 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1100 |
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1101 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1102 |
RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1103 |
if (bg_col.w > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1104 |
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1105 |
window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1106 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1107 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1108 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1109 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1110 |
// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1111 |
bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1112 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1113 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1114 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1115 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1116 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1117 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1118 |
return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1119 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1120 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1121 |
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1122 |
// Legacy API obsoleted in 1.89. Two differences with new ImageButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1123 |
// - old ImageButton() used ImTextureId as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1124 |
// - new ImageButton() requires an explicit 'const char* str_id' |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1125 |
// - old ImageButton() had frame_padding' override argument. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1126 |
// - new ImageButton() always use style.FramePadding. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1127 |
/* |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1128 |
bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1129 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1130 |
// Default to using texture ID as ID. User can still push string/integer prefixes. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1131 |
PushID((ImTextureID)(intptr_t)user_texture_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1132 |
if (frame_padding >= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1133 |
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1134 |
bool ret = ImageButton("", user_texture_id, size, uv0, uv1, bg_col, tint_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1135 |
if (frame_padding >= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1136 |
PopStyleVar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1137 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1138 |
return ret; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1139 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1140 |
*/ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1141 |
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1142 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1143 |
bool ImGui::Checkbox(const char* label, bool* v) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1144 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1145 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1146 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1147 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1148 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1149 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1150 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1151 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1152 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1153 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1154 |
const float square_sz = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1155 |
const ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1156 |
const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1157 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1158 |
const bool is_visible = ItemAdd(total_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1159 |
const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1160 |
if (!is_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1161 |
if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1162 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1163 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1164 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1165 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1166 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1167 |
// Range-Selection/Multi-selection support (header) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1168 |
bool checked = *v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1169 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1170 |
MultiSelectItemHeader(id, &checked, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1171 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1172 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1173 |
bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1174 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1175 |
// Range-Selection/Multi-selection support (footer) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1176 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1177 |
MultiSelectItemFooter(id, &checked, &pressed); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1178 |
else if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1179 |
checked = !checked; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1180 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1181 |
if (*v != checked) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1182 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1183 |
*v = checked; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1184 |
pressed = true; // return value |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1185 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1186 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1187 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1188 |
const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1189 |
const bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1190 |
if (is_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1191 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1192 |
RenderNavCursor(total_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1193 |
RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1194 |
ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1195 |
if (mixed_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1196 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1197 |
// Undocumented tristate/mixed/indeterminate checkbox (#2644) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1198 |
// This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1199 |
ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1200 |
window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1201 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1202 |
else if (*v) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1203 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1204 |
const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1205 |
RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1206 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1207 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1208 |
const ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1209 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1210 |
LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1211 |
if (is_visible && label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1212 |
RenderText(label_pos, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1213 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1214 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1215 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1216 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1217 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1218 |
template<typename T> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1219 |
bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1220 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1221 |
bool all_on = (*flags & flags_value) == flags_value; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1222 |
bool any_on = (*flags & flags_value) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1223 |
bool pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1224 |
if (!all_on && any_on) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1225 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1226 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1227 |
g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1228 |
pressed = Checkbox(label, &all_on); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1229 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1230 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1231 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1232 |
pressed = Checkbox(label, &all_on); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1233 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1234 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1235 |
if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1236 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1237 |
if (all_on) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1238 |
*flags |= flags_value; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1239 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1240 |
*flags &= ~flags_value; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1241 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1242 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1243 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1244 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1245 |
bool ImGui::CheckboxFlags(const char* label, int* flags, int flags_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1246 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1247 |
return CheckboxFlagsT(label, flags, flags_value); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1248 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1249 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1250 |
bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1251 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1252 |
return CheckboxFlagsT(label, flags, flags_value); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1253 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1254 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1255 |
bool ImGui::CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1256 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1257 |
return CheckboxFlagsT(label, flags, flags_value); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1258 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1259 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1260 |
bool ImGui::CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1261 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1262 |
return CheckboxFlagsT(label, flags, flags_value); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1263 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1264 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1265 |
bool ImGui::RadioButton(const char* label, bool active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1266 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1267 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1268 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1269 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1270 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1271 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1272 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1273 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1274 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1275 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1276 |
const float square_sz = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1277 |
const ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1278 |
const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1279 |
const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1280 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1281 |
if (!ItemAdd(total_bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1282 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1283 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1284 |
ImVec2 center = check_bb.GetCenter(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1285 |
center.x = IM_ROUND(center.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1286 |
center.y = IM_ROUND(center.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1287 |
const float radius = (square_sz - 1.0f) * 0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1288 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1289 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1290 |
bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1291 |
if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1292 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1293 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1294 |
RenderNavCursor(total_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1295 |
const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1296 |
window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1297 |
if (active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1298 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1299 |
const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1300 |
window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1301 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1302 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1303 |
if (style.FrameBorderSize > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1304 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1305 |
window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1306 |
window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1307 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1308 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1309 |
ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1310 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1311 |
LogRenderedText(&label_pos, active ? "(x)" : "( )"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1312 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1313 |
RenderText(label_pos, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1314 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1315 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1316 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1317 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1318 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1319 |
// FIXME: This would work nicely if it was a public template, e.g. 'template<T> RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1320 |
bool ImGui::RadioButton(const char* label, int* v, int v_button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1321 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1322 |
const bool pressed = RadioButton(label, *v == v_button); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1323 |
if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1324 |
*v = v_button; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1325 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1326 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1327 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1328 |
// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1329 |
void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1330 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1331 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1332 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1333 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1334 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1335 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1336 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1337 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1338 |
ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1339 |
ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1340 |
ImRect bb(pos, pos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1341 |
ItemSize(size, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1342 |
if (!ItemAdd(bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1343 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1344 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1345 |
// Fraction < 0.0f will display an indeterminate progress bar animation |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1346 |
// The value must be animated along with time, so e.g. passing '-1.0f * ImGui::GetTime()' as fraction works. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1347 |
const bool is_indeterminate = (fraction < 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1348 |
if (!is_indeterminate) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1349 |
fraction = ImSaturate(fraction); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1350 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1351 |
// Out of courtesy we accept a NaN fraction without crashing |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1352 |
float fill_n0 = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1353 |
float fill_n1 = (fraction == fraction) ? fraction : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1354 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1355 |
if (is_indeterminate) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1356 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1357 |
const float fill_width_n = 0.2f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1358 |
fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1359 |
fill_n1 = ImSaturate(fill_n0 + fill_width_n); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1360 |
fill_n0 = ImSaturate(fill_n0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1361 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1362 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1363 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1364 |
RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1365 |
bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1366 |
RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1367 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1368 |
// Default displaying the fraction as percentage string, but user can override it |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1369 |
// Don't display text for indeterminate bars by default |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1370 |
char overlay_buf[32]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1371 |
if (!is_indeterminate || overlay != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1372 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1373 |
if (!overlay) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1374 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1375 |
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1376 |
overlay = overlay_buf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1377 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1378 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1379 |
ImVec2 overlay_size = CalcTextSize(overlay, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1380 |
if (overlay_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1381 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1382 |
float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1383 |
RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1384 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1385 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1386 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1387 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1388 |
void ImGui::Bullet() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1389 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1390 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1391 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1392 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1393 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1394 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1395 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1396 |
const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1397 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1398 |
ItemSize(bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1399 |
if (!ItemAdd(bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1400 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1401 |
SameLine(0, style.FramePadding.x * 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1402 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1403 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1404 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1405 |
// Render and stay on same line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1406 |
ImU32 text_col = GetColorU32(ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1407 |
RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1408 |
SameLine(0, style.FramePadding.x * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1409 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1410 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1411 |
// This is provided as a convenience for being an often requested feature. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1412 |
// FIXME-STYLE: we delayed adding as there is a larger plan to revamp the styling system. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1413 |
// Because of this we currently don't provide many styling options for this widget |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1414 |
// (e.g. hovered/active colors are automatically inferred from a single color). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1415 |
bool ImGui::TextLink(const char* label) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1416 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1417 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1418 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1419 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1420 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1421 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1422 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1423 |
const char* label_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1424 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1425 |
ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1426 |
ImVec2 size = CalcTextSize(label, label_end, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1427 |
ImRect bb(pos, pos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1428 |
ItemSize(size, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1429 |
if (!ItemAdd(bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1430 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1431 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1432 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1433 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1434 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1435 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1436 |
if (hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1437 |
SetMouseCursor(ImGuiMouseCursor_Hand); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1438 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1439 |
ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1440 |
ImVec4 line_colf = text_colf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1441 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1442 |
// FIXME-STYLE: Read comments above. This widget is NOT written in the same style as some earlier widgets, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1443 |
// as we are currently experimenting/planning a different styling system. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1444 |
float h, s, v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1445 |
ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z, h, s, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1446 |
if (held || hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1447 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1448 |
v = ImSaturate(v + (held ? 0.4f : 0.3f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1449 |
h = ImFmod(h + 0.02f, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1450 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1451 |
ColorConvertHSVtoRGB(h, s, v, text_colf.x, text_colf.y, text_colf.z); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1452 |
v = ImSaturate(v - 0.20f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1453 |
ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1454 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1455 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1456 |
float line_y = bb.Max.y + ImFloor(g.Font->Descent * g.FontScale * 0.20f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1457 |
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1458 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1459 |
PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1460 |
RenderText(bb.Min, label, label_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1461 |
PopStyleColor(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1462 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1463 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1464 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1465 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1466 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1467 |
void ImGui::TextLinkOpenURL(const char* label, const char* url) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1468 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1469 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1470 |
if (url == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1471 |
url = label; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1472 |
if (TextLink(label)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1473 |
if (g.PlatformIO.Platform_OpenInShellFn != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1474 |
g.PlatformIO.Platform_OpenInShellFn(&g, url); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1475 |
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1476 |
if (BeginPopupContextItem()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1477 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1478 |
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1479 |
SetClipboardText(url); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1480 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1481 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1482 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1483 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1484 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1485 |
// [SECTION] Widgets: Low-level Layout helpers |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1486 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1487 |
// - Spacing() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1488 |
// - Dummy() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1489 |
// - NewLine() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1490 |
// - AlignTextToFramePadding() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1491 |
// - SeparatorEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1492 |
// - Separator() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1493 |
// - SplitterBehavior() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1494 |
// - ShrinkWidths() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1495 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1496 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1497 |
void ImGui::Spacing() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1498 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1499 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1500 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1501 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1502 |
ItemSize(ImVec2(0, 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1503 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1504 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1505 |
void ImGui::Dummy(const ImVec2& size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1506 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1507 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1508 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1509 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1510 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1511 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1512 |
ItemSize(size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1513 |
ItemAdd(bb, 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1514 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1515 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1516 |
void ImGui::NewLine() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1517 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1518 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1519 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1520 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1521 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1522 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1523 |
const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1524 |
window->DC.LayoutType = ImGuiLayoutType_Vertical; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1525 |
window->DC.IsSameLine = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1526 |
if (window->DC.CurrLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1527 |
ItemSize(ImVec2(0, 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1528 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1529 |
ItemSize(ImVec2(0.0f, g.FontSize)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1530 |
window->DC.LayoutType = backup_layout_type; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1531 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1532 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1533 |
void ImGui::AlignTextToFramePadding() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1534 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1535 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1536 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1537 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1538 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1539 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1540 |
window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1541 |
window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1542 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1543 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1544 |
// Horizontal/vertical separating line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1545 |
// FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1546 |
// Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1547 |
void ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1548 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1549 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1550 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1551 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1552 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1553 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1554 |
IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1555 |
IM_ASSERT(thickness > 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1556 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1557 |
if (flags & ImGuiSeparatorFlags_Vertical) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1558 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1559 |
// Vertical separator, for menu bars (use current line height). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1560 |
float y1 = window->DC.CursorPos.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1561 |
float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1562 |
const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1563 |
ItemSize(ImVec2(thickness, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1564 |
if (!ItemAdd(bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1565 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1566 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1567 |
// Draw |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1568 |
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1569 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1570 |
LogText(" |"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1571 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1572 |
else if (flags & ImGuiSeparatorFlags_Horizontal) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1573 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1574 |
// Horizontal Separator |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1575 |
float x1 = window->DC.CursorPos.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1576 |
float x2 = window->WorkRect.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1577 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1578 |
// Preserve legacy behavior inside Columns() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1579 |
// Before Tables API happened, we relied on Separator() to span all columns of a Columns() set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1580 |
// We currently don't need to provide the same feature for tables because tables naturally have border features. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1581 |
ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1582 |
if (columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1583 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1584 |
x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1585 |
x2 = window->Pos.x + window->Size.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1586 |
PushColumnsBackground(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1587 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1588 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1589 |
// We don't provide our width to the layout so that it doesn't get feed back into AutoFit |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1590 |
// FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1591 |
const float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1592 |
const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1593 |
ItemSize(ImVec2(0.0f, thickness_for_layout)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1594 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1595 |
if (ItemAdd(bb, 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1596 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1597 |
// Draw |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1598 |
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1599 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1600 |
LogRenderedText(&bb.Min, "--------------------------------\n"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1601 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1602 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1603 |
if (columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1604 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1605 |
PopColumnsBackground(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1606 |
columns->LineMinY = window->DC.CursorPos.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1607 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1608 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1609 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1610 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1611 |
void ImGui::Separator() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1612 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1613 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1614 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1615 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1616 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1617 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1618 |
// Those flags should eventually be configurable by the user |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1619 |
// FIXME: We cannot g.Style.SeparatorTextBorderSize for thickness as it relates to SeparatorText() which is a decorated separator, not defaulting to 1.0f. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1620 |
ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1621 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1622 |
// Only applies to legacy Columns() api as they relied on Separator() a lot. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1623 |
if (window->DC.CurrentColumns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1624 |
flags |= ImGuiSeparatorFlags_SpanAllColumns; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1625 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1626 |
SeparatorEx(flags, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1627 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1628 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1629 |
void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1630 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1631 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1632 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1633 |
ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1634 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1635 |
const ImVec2 label_size = CalcTextSize(label, label_end, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1636 |
const ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1637 |
const ImVec2 padding = style.SeparatorTextPadding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1638 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1639 |
const float separator_thickness = style.SeparatorTextBorderSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1640 |
const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1641 |
const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1642 |
const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1643 |
ItemSize(min_size, text_baseline_y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1644 |
if (!ItemAdd(bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1645 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1646 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1647 |
const float sep1_x1 = pos.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1648 |
const float sep2_x2 = bb.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1649 |
const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1650 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1651 |
const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1652 |
const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1653 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1654 |
// This allows using SameLine() to position something in the 'extra_w' |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1655 |
window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1656 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1657 |
const ImU32 separator_col = GetColorU32(ImGuiCol_Separator); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1658 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1659 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1660 |
const float sep1_x2 = label_pos.x - style.ItemSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1661 |
const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1662 |
if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1663 |
window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1664 |
if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1665 |
window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1666 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1667 |
LogSetNextTextDecoration("---", NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1668 |
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1669 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1670 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1671 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1672 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1673 |
LogText("---"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1674 |
if (separator_thickness > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1675 |
window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1676 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1677 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1678 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1679 |
void ImGui::SeparatorText(const char* label) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1680 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1681 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1682 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1683 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1684 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1685 |
// The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1686 |
// - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1687 |
// - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1688 |
// - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...' |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1689 |
// Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1690 |
// and then we can turn this into a format function. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1691 |
SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1692 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1693 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1694 |
// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1695 |
bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1696 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1697 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1698 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1699 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1700 |
if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1701 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1702 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1703 |
// FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1704 |
// to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1705 |
// Nowadays we would instead want to use SetNextItemAllowOverlap() before the item. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1706 |
ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1707 |
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1708 |
button_flags |= ImGuiButtonFlags_AllowOverlap; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1709 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1710 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1711 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1712 |
ImRect bb_interact = bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1713 |
bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1714 |
ButtonBehavior(bb_interact, id, &hovered, &held, button_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1715 |
if (hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1716 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1717 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1718 |
if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1719 |
SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1720 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1721 |
ImRect bb_render = bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1722 |
if (held) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1723 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1724 |
float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1725 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1726 |
// Minimum pane size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1727 |
float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1728 |
float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1729 |
if (mouse_delta < -size_1_maximum_delta) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1730 |
mouse_delta = -size_1_maximum_delta; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1731 |
if (mouse_delta > size_2_maximum_delta) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1732 |
mouse_delta = size_2_maximum_delta; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1733 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1734 |
// Apply resize |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1735 |
if (mouse_delta != 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1736 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1737 |
*size1 = ImMax(*size1 + mouse_delta, min_size1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1738 |
*size2 = ImMax(*size2 - mouse_delta, min_size2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1739 |
bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1740 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1741 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1742 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1743 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1744 |
// Render at new position |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1745 |
if (bg_col & IM_COL32_A_MASK) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1746 |
window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1747 |
const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1748 |
window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1749 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1750 |
return held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1751 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1752 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1753 |
static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1754 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1755 |
const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1756 |
const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1757 |
if (int d = (int)(b->Width - a->Width)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1758 |
return d; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1759 |
return (b->Index - a->Index); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1760 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1761 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1762 |
// Shrink excess width from a set of item, by removing width from the larger items first. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1763 |
// Set items Width to -1.0f to disable shrinking this item. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1764 |
void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1765 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1766 |
if (count == 1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1767 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1768 |
if (items[0].Width >= 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1769 |
items[0].Width = ImMax(items[0].Width - width_excess, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1770 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1771 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1772 |
ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1773 |
int count_same_width = 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1774 |
while (width_excess > 0.0f && count_same_width < count) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1775 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1776 |
while (count_same_width < count && items[0].Width <= items[count_same_width].Width) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1777 |
count_same_width++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1778 |
float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1779 |
if (max_width_to_remove_per_item <= 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1780 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1781 |
float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1782 |
for (int item_n = 0; item_n < count_same_width; item_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1783 |
items[item_n].Width -= width_to_remove_per_item; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1784 |
width_excess -= width_to_remove_per_item * count_same_width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1785 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1786 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1787 |
// Round width and redistribute remainder |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1788 |
// Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1789 |
width_excess = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1790 |
for (int n = 0; n < count; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1791 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1792 |
float width_rounded = ImTrunc(items[n].Width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1793 |
width_excess += items[n].Width - width_rounded; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1794 |
items[n].Width = width_rounded; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1795 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1796 |
while (width_excess > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1797 |
for (int n = 0; n < count && width_excess > 0.0f; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1798 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1799 |
float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1800 |
items[n].Width += width_to_add; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1801 |
width_excess -= width_to_add; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1802 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1803 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1804 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1805 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1806 |
// [SECTION] Widgets: ComboBox |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1807 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1808 |
// - CalcMaxPopupHeightFromItemCount() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1809 |
// - BeginCombo() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1810 |
// - BeginComboPopup() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1811 |
// - EndCombo() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1812 |
// - BeginComboPreview() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1813 |
// - EndComboPreview() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1814 |
// - Combo() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1815 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1816 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1817 |
static float CalcMaxPopupHeightFromItemCount(int items_count) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1818 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1819 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1820 |
if (items_count <= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1821 |
return FLT_MAX; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1822 |
return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1823 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1824 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1825 |
bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1826 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1827 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1828 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1829 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1830 |
ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1831 |
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1832 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1833 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1834 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1835 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1836 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1837 |
IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1838 |
if (flags & ImGuiComboFlags_WidthFitPreview) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1839 |
IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1840 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1841 |
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1842 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1843 |
const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1844 |
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1845 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1846 |
const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1847 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1848 |
if (!ItemAdd(total_bb, id, &bb)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1849 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1850 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1851 |
// Open on click |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1852 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1853 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1854 |
const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1855 |
bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1856 |
if (pressed && !popup_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1857 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1858 |
OpenPopupEx(popup_id, ImGuiPopupFlags_None); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1859 |
popup_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1860 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1861 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1862 |
// Render shape |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1863 |
const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1864 |
const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1865 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1866 |
if (!(flags & ImGuiComboFlags_NoPreview)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1867 |
window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1868 |
if (!(flags & ImGuiComboFlags_NoArrowButton)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1869 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1870 |
ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1871 |
ImU32 text_col = GetColorU32(ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1872 |
window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1873 |
if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1874 |
RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1875 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1876 |
RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1877 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1878 |
// Custom preview |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1879 |
if (flags & ImGuiComboFlags_CustomPreview) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1880 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1881 |
g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1882 |
IM_ASSERT(preview_value == NULL || preview_value[0] == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1883 |
preview_value = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1884 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1885 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1886 |
// Render preview and label |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1887 |
if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1888 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1889 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1890 |
LogSetNextTextDecoration("{", "}"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1891 |
RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1892 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1893 |
if (label_size.x > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1894 |
RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1895 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1896 |
if (!popup_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1897 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1898 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1899 |
g.NextWindowData.Flags = backup_next_window_data_flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1900 |
return BeginComboPopup(popup_id, bb, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1901 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1902 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1903 |
bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1904 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1905 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1906 |
if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1907 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1908 |
g.NextWindowData.ClearFlags(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1909 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1910 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1911 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1912 |
// Set popup size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1913 |
float w = bb.GetWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1914 |
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1915 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1916 |
g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1917 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1918 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1919 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1920 |
if ((flags & ImGuiComboFlags_HeightMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1921 |
flags |= ImGuiComboFlags_HeightRegular; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1922 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1923 |
int popup_max_height_in_items = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1924 |
if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1925 |
else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1926 |
else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1927 |
ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1928 |
if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1929 |
constraint_min.x = w; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1930 |
if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1931 |
constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1932 |
SetNextWindowSizeConstraints(constraint_min, constraint_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1933 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1934 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1935 |
// This is essentially a specialized version of BeginPopupEx() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1936 |
char name[16]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1937 |
ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1938 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1939 |
// Set position given a custom constraint (peak into expected window size so we can position it) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1940 |
// FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1941 |
// FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1942 |
if (ImGuiWindow* popup_window = FindWindowByName(name)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1943 |
if (popup_window->WasActive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1944 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1945 |
// Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1946 |
ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1947 |
popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1948 |
ImRect r_outer = GetPopupAllowedExtentRect(popup_window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1949 |
ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1950 |
SetNextWindowPos(pos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1951 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1952 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1953 |
// We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1954 |
ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1955 |
PushStyleVarX(ImGuiStyleVar_WindowPadding, g.Style.FramePadding.x); // Horizontally align ourselves with the framed text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1956 |
bool ret = Begin(name, NULL, window_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1957 |
PopStyleVar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1958 |
if (!ret) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1959 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1960 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1961 |
IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1962 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1963 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1964 |
g.BeginComboDepth++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1965 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1966 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1967 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1968 |
void ImGui::EndCombo() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1969 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1970 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1971 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1972 |
g.BeginComboDepth--; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1973 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1974 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1975 |
// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1976 |
// (Experimental, see GitHub issues: #1658, #4168) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1977 |
bool ImGui::BeginComboPreview() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1978 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1979 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1980 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1981 |
ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1982 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1983 |
if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1984 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1985 |
IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1986 |
if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1987 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1988 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1989 |
// FIXME: This could be contained in a PushWorkRect() api |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1990 |
preview_data->BackupCursorPos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1991 |
preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1992 |
preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1993 |
preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1994 |
preview_data->BackupLayout = window->DC.LayoutType; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1995 |
window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1996 |
window->DC.CursorMaxPos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1997 |
window->DC.LayoutType = ImGuiLayoutType_Horizontal; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1998 |
window->DC.IsSameLine = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
1999 |
PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2000 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2001 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2002 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2003 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2004 |
void ImGui::EndComboPreview() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2005 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2006 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2007 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2008 |
ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2009 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2010 |
// FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2011 |
ImDrawList* draw_list = window->DrawList; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2012 |
if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2013 |
if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2014 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2015 |
draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2016 |
draw_list->_TryMergeDrawCmds(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2017 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2018 |
PopClipRect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2019 |
window->DC.CursorPos = preview_data->BackupCursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2020 |
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2021 |
window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2022 |
window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2023 |
window->DC.LayoutType = preview_data->BackupLayout; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2024 |
window->DC.IsSameLine = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2025 |
preview_data->PreviewRect = ImRect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2026 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2027 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2028 |
// Getter for the old Combo() API: const char*[] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2029 |
static const char* Items_ArrayGetter(void* data, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2030 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2031 |
const char* const* items = (const char* const*)data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2032 |
return items[idx]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2033 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2034 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2035 |
// Getter for the old Combo() API: "item1\0item2\0item3\0" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2036 |
static const char* Items_SingleStringGetter(void* data, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2037 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2038 |
const char* items_separated_by_zeros = (const char*)data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2039 |
int items_count = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2040 |
const char* p = items_separated_by_zeros; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2041 |
while (*p) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2042 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2043 |
if (idx == items_count) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2044 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2045 |
p += strlen(p) + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2046 |
items_count++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2047 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2048 |
return *p ? p : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2049 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2050 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2051 |
// Old API, prefer using BeginCombo() nowadays if you can. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2052 |
bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2053 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2054 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2055 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2056 |
// Call the getter to obtain the preview string which is a parameter to BeginCombo() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2057 |
const char* preview_value = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2058 |
if (*current_item >= 0 && *current_item < items_count) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2059 |
preview_value = getter(user_data, *current_item); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2060 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2061 |
// The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2062 |
if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2063 |
SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2064 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2065 |
if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2066 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2067 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2068 |
// Display items |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2069 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2070 |
ImGuiListClipper clipper; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2071 |
clipper.Begin(items_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2072 |
clipper.IncludeItemByIndex(*current_item); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2073 |
while (clipper.Step()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2074 |
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2075 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2076 |
const char* item_text = getter(user_data, i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2077 |
if (item_text == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2078 |
item_text = "*Unknown item*"; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2079 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2080 |
PushID(i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2081 |
const bool item_selected = (i == *current_item); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2082 |
if (Selectable(item_text, item_selected) && *current_item != i) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2083 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2084 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2085 |
*current_item = i; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2086 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2087 |
if (item_selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2088 |
SetItemDefaultFocus(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2089 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2090 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2091 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2092 |
EndCombo(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2093 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2094 |
MarkItemEdited(g.LastItemData.ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2095 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2096 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2097 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2098 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2099 |
// Combo box helper allowing to pass an array of strings. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2100 |
bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2101 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2102 |
const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2103 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2104 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2105 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2106 |
// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2107 |
bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2108 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2109 |
int items_count = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2110 |
const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2111 |
while (*p) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2112 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2113 |
p += strlen(p) + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2114 |
items_count++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2115 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2116 |
bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2117 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2118 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2119 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2120 |
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2121 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2122 |
struct ImGuiGetNameFromIndexOldToNewCallbackData { void* UserData; bool (*OldCallback)(void*, int, const char**); }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2123 |
static const char* ImGuiGetNameFromIndexOldToNewCallback(void* user_data, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2124 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2125 |
ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2126 |
const char* s = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2127 |
data->OldCallback(data->UserData, idx, &s); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2128 |
return s; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2129 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2130 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2131 |
bool ImGui::ListBox(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int height_in_items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2132 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2133 |
ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2134 |
return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2135 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2136 |
bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int popup_max_height_in_items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2137 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2138 |
ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2139 |
return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2140 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2141 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2142 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2143 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2144 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2145 |
// [SECTION] Data Type and Data Formatting Helpers [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2146 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2147 |
// - DataTypeGetInfo() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2148 |
// - DataTypeFormatString() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2149 |
// - DataTypeApplyOp() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2150 |
// - DataTypeApplyFromText() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2151 |
// - DataTypeCompare() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2152 |
// - DataTypeClamp() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2153 |
// - GetMinimumStepAtDecimalPrecision |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2154 |
// - RoundScalarWithFormat<>() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2155 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2156 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2157 |
static const ImGuiDataTypeInfo GDataTypeInfo[] = |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2158 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2159 |
{ sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2160 |
{ sizeof(unsigned char), "U8", "%u", "%u" }, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2161 |
{ sizeof(short), "S16", "%d", "%d" }, // ImGuiDataType_S16 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2162 |
{ sizeof(unsigned short), "U16", "%u", "%u" }, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2163 |
{ sizeof(int), "S32", "%d", "%d" }, // ImGuiDataType_S32 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2164 |
{ sizeof(unsigned int), "U32", "%u", "%u" }, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2165 |
#ifdef _MSC_VER |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2166 |
{ sizeof(ImS64), "S64", "%I64d","%I64d" }, // ImGuiDataType_S64 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2167 |
{ sizeof(ImU64), "U64", "%I64u","%I64u" }, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2168 |
#else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2169 |
{ sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2170 |
{ sizeof(ImU64), "U64", "%llu", "%llu" }, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2171 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2172 |
{ sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2173 |
{ sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2174 |
{ sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2175 |
}; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2176 |
IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2177 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2178 |
const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2179 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2180 |
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2181 |
return &GDataTypeInfo[data_type]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2182 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2183 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2184 |
int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2185 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2186 |
// Signedness doesn't matter when pushing integer arguments |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2187 |
if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2188 |
return ImFormatString(buf, buf_size, format, *(const ImU32*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2189 |
if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2190 |
return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2191 |
if (data_type == ImGuiDataType_Float) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2192 |
return ImFormatString(buf, buf_size, format, *(const float*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2193 |
if (data_type == ImGuiDataType_Double) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2194 |
return ImFormatString(buf, buf_size, format, *(const double*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2195 |
if (data_type == ImGuiDataType_S8) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2196 |
return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2197 |
if (data_type == ImGuiDataType_U8) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2198 |
return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2199 |
if (data_type == ImGuiDataType_S16) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2200 |
return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2201 |
if (data_type == ImGuiDataType_U16) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2202 |
return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2203 |
IM_ASSERT(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2204 |
return 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2205 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2206 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2207 |
void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg1, const void* arg2) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2208 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2209 |
IM_ASSERT(op == '+' || op == '-'); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2210 |
switch (data_type) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2211 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2212 |
case ImGuiDataType_S8: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2213 |
if (op == '+') { *(ImS8*)output = ImAddClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2214 |
if (op == '-') { *(ImS8*)output = ImSubClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2215 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2216 |
case ImGuiDataType_U8: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2217 |
if (op == '+') { *(ImU8*)output = ImAddClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2218 |
if (op == '-') { *(ImU8*)output = ImSubClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2219 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2220 |
case ImGuiDataType_S16: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2221 |
if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2222 |
if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2223 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2224 |
case ImGuiDataType_U16: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2225 |
if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2226 |
if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2227 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2228 |
case ImGuiDataType_S32: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2229 |
if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2230 |
if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2231 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2232 |
case ImGuiDataType_U32: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2233 |
if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2234 |
if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2235 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2236 |
case ImGuiDataType_S64: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2237 |
if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2238 |
if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2239 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2240 |
case ImGuiDataType_U64: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2241 |
if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2242 |
if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2243 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2244 |
case ImGuiDataType_Float: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2245 |
if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2246 |
if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2247 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2248 |
case ImGuiDataType_Double: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2249 |
if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2250 |
if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2251 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2252 |
case ImGuiDataType_COUNT: break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2253 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2254 |
IM_ASSERT(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2255 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2256 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2257 |
// User can input math operators (e.g. +100) to edit a numerical values. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2258 |
// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2259 |
bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2260 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2261 |
// Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2262 |
const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2263 |
ImGuiDataTypeStorage data_backup; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2264 |
memcpy(&data_backup, p_data, type_info->Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2265 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2266 |
while (ImCharIsBlankA(*buf)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2267 |
buf++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2268 |
if (!buf[0]) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2269 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2270 |
if (p_data_when_empty != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2271 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2272 |
memcpy(p_data, p_data_when_empty, type_info->Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2273 |
return memcmp(&data_backup, p_data, type_info->Size) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2274 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2275 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2276 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2277 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2278 |
// Sanitize format |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2279 |
// - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2280 |
// - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2281 |
char format_sanitized[32]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2282 |
if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2283 |
format = type_info->ScanFmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2284 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2285 |
format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2286 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2287 |
// Small types need a 32-bit buffer to receive the result from scanf() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2288 |
int v32 = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2289 |
if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2290 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2291 |
if (type_info->Size < 4) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2292 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2293 |
if (data_type == ImGuiDataType_S8) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2294 |
*(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2295 |
else if (data_type == ImGuiDataType_U8) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2296 |
*(ImU8*)p_data = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2297 |
else if (data_type == ImGuiDataType_S16) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2298 |
*(ImS16*)p_data = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2299 |
else if (data_type == ImGuiDataType_U16) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2300 |
*(ImU16*)p_data = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2301 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2302 |
IM_ASSERT(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2303 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2304 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2305 |
return memcmp(&data_backup, p_data, type_info->Size) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2306 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2307 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2308 |
template<typename T> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2309 |
static int DataTypeCompareT(const T* lhs, const T* rhs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2310 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2311 |
if (*lhs < *rhs) return -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2312 |
if (*lhs > *rhs) return +1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2313 |
return 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2314 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2315 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2316 |
int ImGui::DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2317 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2318 |
switch (data_type) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2319 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2320 |
case ImGuiDataType_S8: return DataTypeCompareT<ImS8 >((const ImS8* )arg_1, (const ImS8* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2321 |
case ImGuiDataType_U8: return DataTypeCompareT<ImU8 >((const ImU8* )arg_1, (const ImU8* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2322 |
case ImGuiDataType_S16: return DataTypeCompareT<ImS16 >((const ImS16* )arg_1, (const ImS16* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2323 |
case ImGuiDataType_U16: return DataTypeCompareT<ImU16 >((const ImU16* )arg_1, (const ImU16* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2324 |
case ImGuiDataType_S32: return DataTypeCompareT<ImS32 >((const ImS32* )arg_1, (const ImS32* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2325 |
case ImGuiDataType_U32: return DataTypeCompareT<ImU32 >((const ImU32* )arg_1, (const ImU32* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2326 |
case ImGuiDataType_S64: return DataTypeCompareT<ImS64 >((const ImS64* )arg_1, (const ImS64* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2327 |
case ImGuiDataType_U64: return DataTypeCompareT<ImU64 >((const ImU64* )arg_1, (const ImU64* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2328 |
case ImGuiDataType_Float: return DataTypeCompareT<float >((const float* )arg_1, (const float* )arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2329 |
case ImGuiDataType_Double: return DataTypeCompareT<double>((const double*)arg_1, (const double*)arg_2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2330 |
case ImGuiDataType_COUNT: break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2331 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2332 |
IM_ASSERT(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2333 |
return 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2334 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2335 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2336 |
template<typename T> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2337 |
static bool DataTypeClampT(T* v, const T* v_min, const T* v_max) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2338 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2339 |
// Clamp, both sides are optional, return true if modified |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2340 |
if (v_min && *v < *v_min) { *v = *v_min; return true; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2341 |
if (v_max && *v > *v_max) { *v = *v_max; return true; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2342 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2343 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2344 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2345 |
bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2346 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2347 |
switch (data_type) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2348 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2349 |
case ImGuiDataType_S8: return DataTypeClampT<ImS8 >((ImS8* )p_data, (const ImS8* )p_min, (const ImS8* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2350 |
case ImGuiDataType_U8: return DataTypeClampT<ImU8 >((ImU8* )p_data, (const ImU8* )p_min, (const ImU8* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2351 |
case ImGuiDataType_S16: return DataTypeClampT<ImS16 >((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2352 |
case ImGuiDataType_U16: return DataTypeClampT<ImU16 >((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2353 |
case ImGuiDataType_S32: return DataTypeClampT<ImS32 >((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2354 |
case ImGuiDataType_U32: return DataTypeClampT<ImU32 >((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2355 |
case ImGuiDataType_S64: return DataTypeClampT<ImS64 >((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2356 |
case ImGuiDataType_U64: return DataTypeClampT<ImU64 >((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2357 |
case ImGuiDataType_Float: return DataTypeClampT<float >((float* )p_data, (const float* )p_min, (const float* )p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2358 |
case ImGuiDataType_Double: return DataTypeClampT<double>((double*)p_data, (const double*)p_min, (const double*)p_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2359 |
case ImGuiDataType_COUNT: break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2360 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2361 |
IM_ASSERT(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2362 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2363 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2364 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2365 |
bool ImGui::DataTypeIsZero(ImGuiDataType data_type, const void* p_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2366 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2367 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2368 |
return DataTypeCompare(data_type, p_data, &g.DataTypeZeroValue) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2369 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2370 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2371 |
static float GetMinimumStepAtDecimalPrecision(int decimal_precision) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2372 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2373 |
static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2374 |
if (decimal_precision < 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2375 |
return FLT_MIN; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2376 |
return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2377 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2378 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2379 |
template<typename TYPE> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2380 |
TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2381 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2382 |
IM_UNUSED(data_type); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2383 |
IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2384 |
const char* fmt_start = ImParseFormatFindStart(format); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2385 |
if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2386 |
return v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2387 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2388 |
// Sanitize format |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2389 |
char fmt_sanitized[32]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2390 |
ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2391 |
fmt_start = fmt_sanitized; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2392 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2393 |
// Format value with our rounding, and read back |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2394 |
char v_str[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2395 |
ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2396 |
const char* p = v_str; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2397 |
while (*p == ' ') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2398 |
p++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2399 |
v = (TYPE)ImAtof(p); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2400 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2401 |
return v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2402 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2403 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2404 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2405 |
// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2406 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2407 |
// - DragBehaviorT<>() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2408 |
// - DragBehavior() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2409 |
// - DragScalar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2410 |
// - DragScalarN() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2411 |
// - DragFloat() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2412 |
// - DragFloat2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2413 |
// - DragFloat3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2414 |
// - DragFloat4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2415 |
// - DragFloatRange2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2416 |
// - DragInt() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2417 |
// - DragInt2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2418 |
// - DragInt3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2419 |
// - DragInt4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2420 |
// - DragIntRange2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2421 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2422 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2423 |
// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2424 |
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2425 |
bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2426 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2427 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2428 |
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2429 |
const bool is_bounded = (v_min < v_max) || ((v_min == v_max) && (v_min != 0.0f || (flags & ImGuiSliderFlags_ClampZeroRange))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2430 |
const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2431 |
const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2432 |
const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2433 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2434 |
// Default tweak speed |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2435 |
if (v_speed == 0.0f && is_bounded && (v_max - v_min < FLT_MAX)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2436 |
v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2437 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2438 |
// Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2439 |
float adjust_delta = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2440 |
if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2441 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2442 |
adjust_delta = g.IO.MouseDelta[axis]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2443 |
if (g.IO.KeyAlt) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2444 |
adjust_delta *= 1.0f / 100.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2445 |
if (g.IO.KeyShift) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2446 |
adjust_delta *= 10.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2447 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2448 |
else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2449 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2450 |
const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2451 |
const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2452 |
const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2453 |
const float tweak_factor = tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2454 |
adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2455 |
v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2456 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2457 |
adjust_delta *= v_speed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2458 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2459 |
// For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2460 |
if (axis == ImGuiAxis_Y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2461 |
adjust_delta = -adjust_delta; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2462 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2463 |
// For logarithmic use our range is effectively 0..1 so scale the delta into that range |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2464 |
if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2465 |
adjust_delta /= (float)(v_max - v_min); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2466 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2467 |
// Clear current value on activation |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2468 |
// Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2469 |
const bool is_just_activated = g.ActiveIdIsJustActivated; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2470 |
const bool is_already_past_limits_and_pushing_outward = is_bounded && !is_wrapped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2471 |
if (is_just_activated || is_already_past_limits_and_pushing_outward) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2472 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2473 |
g.DragCurrentAccum = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2474 |
g.DragCurrentAccumDirty = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2475 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2476 |
else if (adjust_delta != 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2477 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2478 |
g.DragCurrentAccum += adjust_delta; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2479 |
g.DragCurrentAccumDirty = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2480 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2481 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2482 |
if (!g.DragCurrentAccumDirty) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2483 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2484 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2485 |
TYPE v_cur = *v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2486 |
FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2487 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2488 |
float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2489 |
const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2490 |
if (is_logarithmic) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2491 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2492 |
// When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2493 |
const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2494 |
logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2495 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2496 |
// Convert to parametric space, apply delta, convert back |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2497 |
float v_old_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2498 |
float v_new_parametric = v_old_parametric + g.DragCurrentAccum; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2499 |
v_cur = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2500 |
v_old_ref_for_accum_remainder = v_old_parametric; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2501 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2502 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2503 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2504 |
v_cur += (SIGNEDTYPE)g.DragCurrentAccum; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2505 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2506 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2507 |
// Round to user desired precision based on format string |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2508 |
if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2509 |
v_cur = RoundScalarWithFormatT<TYPE>(format, data_type, v_cur); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2510 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2511 |
// Preserve remainder after rounding has been applied. This also allow slow tweaking of values. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2512 |
g.DragCurrentAccumDirty = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2513 |
if (is_logarithmic) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2514 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2515 |
// Convert to parametric space, apply delta, convert back |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2516 |
float v_new_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2517 |
g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2518 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2519 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2520 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2521 |
g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2522 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2523 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2524 |
// Lose zero sign for float/double |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2525 |
if (v_cur == (TYPE)-0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2526 |
v_cur = (TYPE)0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2527 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2528 |
if (*v != v_cur && is_bounded) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2529 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2530 |
if (is_wrapped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2531 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2532 |
// Wrap values |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2533 |
if (v_cur < v_min) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2534 |
v_cur += v_max - v_min + (is_floating_point ? 0 : 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2535 |
if (v_cur > v_max) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2536 |
v_cur -= v_max - v_min + (is_floating_point ? 0 : 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2537 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2538 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2539 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2540 |
// Clamp values + handle overflow/wrap-around for integer types. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2541 |
if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2542 |
v_cur = v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2543 |
if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2544 |
v_cur = v_max; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2545 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2546 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2547 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2548 |
// Apply result |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2549 |
if (*v == v_cur) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2550 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2551 |
*v = v_cur; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2552 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2553 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2554 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2555 |
bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2556 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2557 |
// Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2558 |
IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2559 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2560 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2561 |
if (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2562 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2563 |
// Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2564 |
if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2565 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2566 |
else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2567 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2568 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2569 |
if (g.ActiveId != id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2570 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2571 |
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2572 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2573 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2574 |
switch (data_type) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2575 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2576 |
case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN, p_max ? *(const ImS8*)p_max : IM_S8_MAX, format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2577 |
case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN, p_max ? *(const ImU8*)p_max : IM_U8_MAX, format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2578 |
case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2579 |
case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2580 |
case ImGuiDataType_S32: return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)p_v, v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2581 |
case ImGuiDataType_U32: return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)p_v, v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2582 |
case ImGuiDataType_S64: return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)p_v, v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2583 |
case ImGuiDataType_U64: return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)p_v, v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2584 |
case ImGuiDataType_Float: return DragBehaviorT<float, float, float >(data_type, (float*)p_v, v_speed, p_min ? *(const float* )p_min : -FLT_MAX, p_max ? *(const float* )p_max : FLT_MAX, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2585 |
case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX, p_max ? *(const double*)p_max : DBL_MAX, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2586 |
case ImGuiDataType_COUNT: break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2587 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2588 |
IM_ASSERT(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2589 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2590 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2591 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2592 |
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2593 |
// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2594 |
bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2595 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2596 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2597 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2598 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2599 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2600 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2601 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2602 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2603 |
const float w = CalcItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2604 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2605 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2606 |
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2607 |
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2608 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2609 |
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2610 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2611 |
if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2612 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2613 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2614 |
// Default format string when passing NULL |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2615 |
if (format == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2616 |
format = DataTypeGetInfo(data_type)->PrintFmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2617 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2618 |
const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2619 |
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2620 |
if (!temp_input_is_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2621 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2622 |
// Tabbing or CTRL-clicking on Drag turns it into an InputText |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2623 |
const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2624 |
const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2625 |
const bool make_active = (clicked || double_clicked || g.NavActivateId == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2626 |
if (make_active && (clicked || double_clicked)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2627 |
SetKeyOwner(ImGuiKey_MouseLeft, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2628 |
if (make_active && temp_input_allowed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2629 |
if ((clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2630 |
temp_input_is_active = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2631 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2632 |
// (Optional) simple click (without moving) turns Drag into an InputText |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2633 |
if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2634 |
if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2635 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2636 |
g.NavActivateId = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2637 |
g.NavActivateFlags = ImGuiActivateFlags_PreferInput; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2638 |
temp_input_is_active = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2639 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2640 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2641 |
if (make_active && !temp_input_is_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2642 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2643 |
SetActiveID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2644 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2645 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2646 |
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2647 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2648 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2649 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2650 |
if (temp_input_is_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2651 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2652 |
// Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2653 |
bool clamp_enabled = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2654 |
if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2655 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2656 |
const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? DataTypeCompare(data_type, p_min, p_max) : 0; // -1 when *p_min < *p_max, == 0 when *p_min == *p_max |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2657 |
if (p_min == NULL || p_max == NULL || clamp_range_dir < 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2658 |
clamp_enabled = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2659 |
else if (clamp_range_dir == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2660 |
clamp_enabled = DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) : true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2661 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2662 |
return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2663 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2664 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2665 |
// Draw frame |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2666 |
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2667 |
RenderNavCursor(frame_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2668 |
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2669 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2670 |
// Drag behavior |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2671 |
const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2672 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2673 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2674 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2675 |
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2676 |
char value_buf[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2677 |
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2678 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2679 |
LogSetNextTextDecoration("{", "}"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2680 |
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2681 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2682 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2683 |
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2684 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2685 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2686 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2687 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2688 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2689 |
bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2690 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2691 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2692 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2693 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2694 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2695 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2696 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2697 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2698 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2699 |
PushMultiItemsWidths(components, CalcItemWidth()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2700 |
size_t type_size = GDataTypeInfo[data_type].Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2701 |
for (int i = 0; i < components; i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2702 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2703 |
PushID(i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2704 |
if (i > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2705 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2706 |
value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2707 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2708 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2709 |
p_data = (void*)((char*)p_data + type_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2710 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2711 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2712 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2713 |
const char* label_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2714 |
if (label != label_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2715 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2716 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2717 |
TextEx(label, label_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2718 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2719 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2720 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2721 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2722 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2723 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2724 |
bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2725 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2726 |
return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2727 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2728 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2729 |
bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2730 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2731 |
return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2732 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2733 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2734 |
bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2735 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2736 |
return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2737 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2738 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2739 |
bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2740 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2741 |
return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2742 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2743 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2744 |
// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2745 |
bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2746 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2747 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2748 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2749 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2750 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2751 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2752 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2753 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2754 |
PushMultiItemsWidths(2, CalcItemWidth()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2755 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2756 |
float min_min = (v_min >= v_max) ? -FLT_MAX : v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2757 |
float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2758 |
ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2759 |
bool value_changed = DragScalar("##min", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2760 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2761 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2762 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2763 |
float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2764 |
float max_max = (v_min >= v_max) ? FLT_MAX : v_max; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2765 |
ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2766 |
value_changed |= DragScalar("##max", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2767 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2768 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2769 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2770 |
TextEx(label, FindRenderedTextEnd(label)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2771 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2772 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2773 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2774 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2775 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2776 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2777 |
// NB: v_speed is float to allow adjusting the drag speed with more precision |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2778 |
bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2779 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2780 |
return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2781 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2782 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2783 |
bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2784 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2785 |
return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2786 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2787 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2788 |
bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2789 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2790 |
return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2791 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2792 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2793 |
bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2794 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2795 |
return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2796 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2797 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2798 |
// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2799 |
bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2800 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2801 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2802 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2803 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2804 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2805 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2806 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2807 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2808 |
PushMultiItemsWidths(2, CalcItemWidth()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2809 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2810 |
int min_min = (v_min >= v_max) ? INT_MIN : v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2811 |
int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2812 |
ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2813 |
bool value_changed = DragInt("##min", v_current_min, v_speed, min_min, min_max, format, min_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2814 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2815 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2816 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2817 |
int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2818 |
int max_max = (v_min >= v_max) ? INT_MAX : v_max; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2819 |
ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2820 |
value_changed |= DragInt("##max", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2821 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2822 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2823 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2824 |
TextEx(label, FindRenderedTextEnd(label)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2825 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2826 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2827 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2828 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2829 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2830 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2831 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2832 |
// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2833 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2834 |
// - ScaleRatioFromValueT<> [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2835 |
// - ScaleValueFromRatioT<> [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2836 |
// - SliderBehaviorT<>() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2837 |
// - SliderBehavior() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2838 |
// - SliderScalar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2839 |
// - SliderScalarN() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2840 |
// - SliderFloat() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2841 |
// - SliderFloat2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2842 |
// - SliderFloat3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2843 |
// - SliderFloat4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2844 |
// - SliderAngle() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2845 |
// - SliderInt() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2846 |
// - SliderInt2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2847 |
// - SliderInt3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2848 |
// - SliderInt4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2849 |
// - VSliderScalar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2850 |
// - VSliderFloat() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2851 |
// - VSliderInt() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2852 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2853 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2854 |
// Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2855 |
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2856 |
float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2857 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2858 |
if (v_min == v_max) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2859 |
return 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2860 |
IM_UNUSED(data_type); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2861 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2862 |
const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2863 |
if (is_logarithmic) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2864 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2865 |
bool flipped = v_max < v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2866 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2867 |
if (flipped) // Handle the case where the range is backwards |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2868 |
ImSwap(v_min, v_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2869 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2870 |
// Fudge min/max to avoid getting close to log(0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2871 |
FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2872 |
FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2873 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2874 |
// Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2875 |
if ((v_min == 0.0f) && (v_max < 0.0f)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2876 |
v_min_fudged = -logarithmic_zero_epsilon; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2877 |
else if ((v_max == 0.0f) && (v_min < 0.0f)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2878 |
v_max_fudged = -logarithmic_zero_epsilon; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2879 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2880 |
float result; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2881 |
if (v_clamped <= v_min_fudged) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2882 |
result = 0.0f; // Workaround for values that are in-range but below our fudge |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2883 |
else if (v_clamped >= v_max_fudged) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2884 |
result = 1.0f; // Workaround for values that are in-range but above our fudge |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2885 |
else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2886 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2887 |
float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space. There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2888 |
float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2889 |
float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2890 |
if (v == 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2891 |
result = zero_point_center; // Special case for exactly zero |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2892 |
else if (v < 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2893 |
result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2894 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2895 |
result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2896 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2897 |
else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2898 |
result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2899 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2900 |
result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2901 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2902 |
return flipped ? (1.0f - result) : result; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2903 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2904 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2905 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2906 |
// Linear slider |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2907 |
return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2908 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2909 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2910 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2911 |
// Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2912 |
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2913 |
TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2914 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2915 |
// We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2916 |
// but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2917 |
if (t <= 0.0f || v_min == v_max) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2918 |
return v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2919 |
if (t >= 1.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2920 |
return v_max; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2921 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2922 |
TYPE result = (TYPE)0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2923 |
if (is_logarithmic) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2924 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2925 |
// Fudge min/max to avoid getting silly results close to zero |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2926 |
FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2927 |
FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2928 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2929 |
const bool flipped = v_max < v_min; // Check if range is "backwards" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2930 |
if (flipped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2931 |
ImSwap(v_min_fudged, v_max_fudged); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2932 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2933 |
// Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2934 |
if ((v_max == 0.0f) && (v_min < 0.0f)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2935 |
v_max_fudged = -logarithmic_zero_epsilon; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2936 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2937 |
float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2938 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2939 |
if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2940 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2941 |
float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2942 |
float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2943 |
float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2944 |
if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2945 |
result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2946 |
else if (t_with_flip < zero_point_center) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2947 |
result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L)))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2948 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2949 |
result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R)))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2950 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2951 |
else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2952 |
result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2953 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2954 |
result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2955 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2956 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2957 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2958 |
// Linear slider |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2959 |
const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2960 |
if (is_floating_point) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2961 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2962 |
result = ImLerp(v_min, v_max, t); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2963 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2964 |
else if (t < 1.0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2965 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2966 |
// - For integer values we want the clicking position to match the grab box so we round above |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2967 |
// This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2968 |
// - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2969 |
// range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2970 |
FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2971 |
result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2972 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2973 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2974 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2975 |
return result; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2976 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2977 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2978 |
// FIXME: Try to move more of the code into shared SliderBehavior() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2979 |
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2980 |
bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2981 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2982 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2983 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2984 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2985 |
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2986 |
const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2987 |
const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2988 |
const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2989 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2990 |
// Calculate bounds |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2991 |
const float grab_padding = 2.0f; // FIXME: Should be part of style. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2992 |
const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2993 |
float grab_sz = style.GrabMinSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2994 |
if (!is_floating_point && v_range_f >= 0.0f) // v_range_f < 0 may happen on integer overflows |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2995 |
grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2996 |
grab_sz = ImMin(grab_sz, slider_sz); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2997 |
const float slider_usable_sz = slider_sz - grab_sz; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2998 |
const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
2999 |
const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3000 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3001 |
float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3002 |
float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3003 |
if (is_logarithmic) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3004 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3005 |
// When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3006 |
const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3007 |
logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3008 |
zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3009 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3010 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3011 |
// Process interacting with the slider |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3012 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3013 |
if (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3014 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3015 |
bool set_new_value = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3016 |
float clicked_t = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3017 |
if (g.ActiveIdSource == ImGuiInputSource_Mouse) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3018 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3019 |
if (!g.IO.MouseDown[0]) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3020 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3021 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3022 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3023 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3024 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3025 |
const float mouse_abs_pos = g.IO.MousePos[axis]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3026 |
if (g.ActiveIdIsJustActivated) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3027 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3028 |
float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3029 |
if (axis == ImGuiAxis_Y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3030 |
grab_t = 1.0f - grab_t; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3031 |
const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3032 |
const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3033 |
g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3034 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3035 |
if (slider_usable_sz > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3036 |
clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3037 |
if (axis == ImGuiAxis_Y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3038 |
clicked_t = 1.0f - clicked_t; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3039 |
set_new_value = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3040 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3041 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3042 |
else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3043 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3044 |
if (g.ActiveIdIsJustActivated) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3045 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3046 |
g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3047 |
g.SliderCurrentAccumDirty = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3048 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3049 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3050 |
float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3051 |
if (input_delta != 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3052 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3053 |
const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3054 |
const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3055 |
const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3056 |
if (decimal_precision > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3057 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3058 |
input_delta /= 100.0f; // Keyboard/Gamepad tweak speeds in % of slider bounds |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3059 |
if (tweak_slow) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3060 |
input_delta /= 10.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3061 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3062 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3063 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3064 |
if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3065 |
input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Keyboard/Gamepad tweak speeds in integer steps |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3066 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3067 |
input_delta /= 100.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3068 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3069 |
if (tweak_fast) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3070 |
input_delta *= 10.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3071 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3072 |
g.SliderCurrentAccum += input_delta; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3073 |
g.SliderCurrentAccumDirty = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3074 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3075 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3076 |
float delta = g.SliderCurrentAccum; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3077 |
if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3078 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3079 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3080 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3081 |
else if (g.SliderCurrentAccumDirty) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3082 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3083 |
clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3084 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3085 |
if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3086 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3087 |
set_new_value = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3088 |
g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3089 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3090 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3091 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3092 |
set_new_value = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3093 |
float old_clicked_t = clicked_t; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3094 |
clicked_t = ImSaturate(clicked_t + delta); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3095 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3096 |
// Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3097 |
TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3098 |
if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3099 |
v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3100 |
float new_clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3101 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3102 |
if (delta > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3103 |
g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3104 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3105 |
g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3106 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3107 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3108 |
g.SliderCurrentAccumDirty = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3109 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3110 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3111 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3112 |
if (set_new_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3113 |
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3114 |
set_new_value = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3115 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3116 |
if (set_new_value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3117 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3118 |
TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3119 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3120 |
// Round to user desired precision based on format string |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3121 |
if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3122 |
v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3123 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3124 |
// Apply result |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3125 |
if (*v != v_new) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3126 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3127 |
*v = v_new; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3128 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3129 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3130 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3131 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3132 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3133 |
if (slider_sz < 1.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3134 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3135 |
*out_grab_bb = ImRect(bb.Min, bb.Min); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3136 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3137 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3138 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3139 |
// Output grab position so it can be displayed by the caller |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3140 |
float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3141 |
if (axis == ImGuiAxis_Y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3142 |
grab_t = 1.0f - grab_t; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3143 |
const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3144 |
if (axis == ImGuiAxis_X) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3145 |
*out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3146 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3147 |
*out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3148 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3149 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3150 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3151 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3152 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3153 |
// For 32-bit and larger types, slider bounds are limited to half the natural type range. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3154 |
// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3155 |
// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3156 |
bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3157 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3158 |
// Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3159 |
IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3160 |
IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3161 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3162 |
switch (data_type) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3163 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3164 |
case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3165 |
case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min, *(const ImU8*)p_max, format, flags, out_grab_bb); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3166 |
case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3167 |
case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3168 |
case ImGuiDataType_S32: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3169 |
IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3170 |
return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)p_v, *(const ImS32*)p_min, *(const ImS32*)p_max, format, flags, out_grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3171 |
case ImGuiDataType_U32: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3172 |
IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3173 |
return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)p_v, *(const ImU32*)p_min, *(const ImU32*)p_max, format, flags, out_grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3174 |
case ImGuiDataType_S64: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3175 |
IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3176 |
return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)p_v, *(const ImS64*)p_min, *(const ImS64*)p_max, format, flags, out_grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3177 |
case ImGuiDataType_U64: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3178 |
IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3179 |
return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)p_v, *(const ImU64*)p_min, *(const ImU64*)p_max, format, flags, out_grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3180 |
case ImGuiDataType_Float: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3181 |
IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3182 |
return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)p_v, *(const float*)p_min, *(const float*)p_max, format, flags, out_grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3183 |
case ImGuiDataType_Double: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3184 |
IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3185 |
return SliderBehaviorT<double, double, double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3186 |
case ImGuiDataType_COUNT: break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3187 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3188 |
IM_ASSERT(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3189 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3190 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3191 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3192 |
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3193 |
// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3194 |
bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3195 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3196 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3197 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3198 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3199 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3200 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3201 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3202 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3203 |
const float w = CalcItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3204 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3205 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3206 |
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3207 |
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3208 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3209 |
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3210 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3211 |
if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3212 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3213 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3214 |
// Default format string when passing NULL |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3215 |
if (format == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3216 |
format = DataTypeGetInfo(data_type)->PrintFmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3217 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3218 |
const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3219 |
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3220 |
if (!temp_input_is_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3221 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3222 |
// Tabbing or CTRL-clicking on Slider turns it into an input box |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3223 |
const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3224 |
const bool make_active = (clicked || g.NavActivateId == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3225 |
if (make_active && clicked) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3226 |
SetKeyOwner(ImGuiKey_MouseLeft, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3227 |
if (make_active && temp_input_allowed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3228 |
if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3229 |
temp_input_is_active = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3230 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3231 |
if (make_active && !temp_input_is_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3232 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3233 |
SetActiveID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3234 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3235 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3236 |
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3237 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3238 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3239 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3240 |
if (temp_input_is_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3241 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3242 |
// Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3243 |
const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3244 |
return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3245 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3246 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3247 |
// Draw frame |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3248 |
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3249 |
RenderNavCursor(frame_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3250 |
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3251 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3252 |
// Slider behavior |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3253 |
ImRect grab_bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3254 |
const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3255 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3256 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3257 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3258 |
// Render grab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3259 |
if (grab_bb.Max.x > grab_bb.Min.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3260 |
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3261 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3262 |
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3263 |
char value_buf[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3264 |
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3265 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3266 |
LogSetNextTextDecoration("{", "}"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3267 |
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3268 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3269 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3270 |
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3271 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3272 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3273 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3274 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3275 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3276 |
// Add multiple sliders on 1 line for compact edition of multiple components |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3277 |
bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3278 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3279 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3280 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3281 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3282 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3283 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3284 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3285 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3286 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3287 |
PushMultiItemsWidths(components, CalcItemWidth()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3288 |
size_t type_size = GDataTypeInfo[data_type].Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3289 |
for (int i = 0; i < components; i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3290 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3291 |
PushID(i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3292 |
if (i > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3293 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3294 |
value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3295 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3296 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3297 |
v = (void*)((char*)v + type_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3298 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3299 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3300 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3301 |
const char* label_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3302 |
if (label != label_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3303 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3304 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3305 |
TextEx(label, label_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3306 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3307 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3308 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3309 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3310 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3311 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3312 |
bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3313 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3314 |
return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3315 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3316 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3317 |
bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3318 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3319 |
return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3320 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3321 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3322 |
bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3323 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3324 |
return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3325 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3326 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3327 |
bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3328 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3329 |
return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3330 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3331 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3332 |
bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3333 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3334 |
if (format == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3335 |
format = "%.0f deg"; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3336 |
float v_deg = (*v_rad) * 360.0f / (2 * IM_PI); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3337 |
bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3338 |
*v_rad = v_deg * (2 * IM_PI) / 360.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3339 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3340 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3341 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3342 |
bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3343 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3344 |
return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3345 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3346 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3347 |
bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3348 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3349 |
return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3350 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3351 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3352 |
bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3353 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3354 |
return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3355 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3356 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3357 |
bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3358 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3359 |
return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3360 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3361 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3362 |
bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3363 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3364 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3365 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3366 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3367 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3368 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3369 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3370 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3371 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3372 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3373 |
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3374 |
const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3375 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3376 |
ItemSize(bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3377 |
if (!ItemAdd(frame_bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3378 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3379 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3380 |
// Default format string when passing NULL |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3381 |
if (format == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3382 |
format = DataTypeGetInfo(data_type)->PrintFmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3383 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3384 |
const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3385 |
const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3386 |
if (clicked || g.NavActivateId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3387 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3388 |
if (clicked) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3389 |
SetKeyOwner(ImGuiKey_MouseLeft, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3390 |
SetActiveID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3391 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3392 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3393 |
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3394 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3395 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3396 |
// Draw frame |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3397 |
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3398 |
RenderNavCursor(frame_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3399 |
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3400 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3401 |
// Slider behavior |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3402 |
ImRect grab_bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3403 |
const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3404 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3405 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3406 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3407 |
// Render grab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3408 |
if (grab_bb.Max.y > grab_bb.Min.y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3409 |
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3410 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3411 |
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3412 |
// For the vertical slider we allow centered text to overlap the frame padding |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3413 |
char value_buf[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3414 |
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3415 |
RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3416 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3417 |
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3418 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3419 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3420 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3421 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3422 |
bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3423 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3424 |
return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3425 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3426 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3427 |
bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3428 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3429 |
return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3430 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3431 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3432 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3433 |
// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3434 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3435 |
// - ImParseFormatFindStart() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3436 |
// - ImParseFormatFindEnd() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3437 |
// - ImParseFormatTrimDecorations() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3438 |
// - ImParseFormatSanitizeForPrinting() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3439 |
// - ImParseFormatSanitizeForScanning() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3440 |
// - ImParseFormatPrecision() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3441 |
// - TempInputTextScalar() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3442 |
// - InputScalar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3443 |
// - InputScalarN() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3444 |
// - InputFloat() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3445 |
// - InputFloat2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3446 |
// - InputFloat3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3447 |
// - InputFloat4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3448 |
// - InputInt() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3449 |
// - InputInt2() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3450 |
// - InputInt3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3451 |
// - InputInt4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3452 |
// - InputDouble() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3453 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3454 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3455 |
// We don't use strchr() because our strings are usually very short and often start with '%' |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3456 |
const char* ImParseFormatFindStart(const char* fmt) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3457 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3458 |
while (char c = fmt[0]) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3459 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3460 |
if (c == '%' && fmt[1] != '%') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3461 |
return fmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3462 |
else if (c == '%') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3463 |
fmt++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3464 |
fmt++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3465 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3466 |
return fmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3467 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3468 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3469 |
const char* ImParseFormatFindEnd(const char* fmt) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3470 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3471 |
// Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3472 |
if (fmt[0] != '%') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3473 |
return fmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3474 |
const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3475 |
const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3476 |
for (char c; (c = *fmt) != 0; fmt++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3477 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3478 |
if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3479 |
return fmt + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3480 |
if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3481 |
return fmt + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3482 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3483 |
return fmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3484 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3485 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3486 |
// Extract the format out of a format string with leading or trailing decorations |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3487 |
// fmt = "blah blah" -> return "" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3488 |
// fmt = "%.3f" -> return fmt |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3489 |
// fmt = "hello %.3f" -> return fmt + 6 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3490 |
// fmt = "%.3f hello" -> return buf written with "%.3f" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3491 |
const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3492 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3493 |
const char* fmt_start = ImParseFormatFindStart(fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3494 |
if (fmt_start[0] != '%') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3495 |
return ""; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3496 |
const char* fmt_end = ImParseFormatFindEnd(fmt_start); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3497 |
if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3498 |
return fmt_start; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3499 |
ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3500 |
return buf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3501 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3502 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3503 |
// Sanitize format |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3504 |
// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3505 |
// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3506 |
void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3507 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3508 |
const char* fmt_end = ImParseFormatFindEnd(fmt_in); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3509 |
IM_UNUSED(fmt_out_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3510 |
IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3511 |
while (fmt_in < fmt_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3512 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3513 |
char c = *fmt_in++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3514 |
if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3515 |
*(fmt_out++) = c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3516 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3517 |
*fmt_out = 0; // Zero-terminate |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3518 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3519 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3520 |
// - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3521 |
const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3522 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3523 |
const char* fmt_end = ImParseFormatFindEnd(fmt_in); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3524 |
const char* fmt_out_begin = fmt_out; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3525 |
IM_UNUSED(fmt_out_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3526 |
IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3527 |
bool has_type = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3528 |
while (fmt_in < fmt_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3529 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3530 |
char c = *fmt_in++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3531 |
if (!has_type && ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '#')) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3532 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3533 |
has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3534 |
if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3535 |
*(fmt_out++) = c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3536 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3537 |
*fmt_out = 0; // Zero-terminate |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3538 |
return fmt_out_begin; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3539 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3540 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3541 |
template<typename TYPE> |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3542 |
static const char* ImAtoi(const char* src, TYPE* output) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3543 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3544 |
int negative = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3545 |
if (*src == '-') { negative = 1; src++; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3546 |
if (*src == '+') { src++; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3547 |
TYPE v = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3548 |
while (*src >= '0' && *src <= '9') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3549 |
v = (v * 10) + (*src++ - '0'); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3550 |
*output = negative ? -v : v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3551 |
return src; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3552 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3553 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3554 |
// Parse display precision back from the display format string |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3555 |
// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3556 |
int ImParseFormatPrecision(const char* fmt, int default_precision) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3557 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3558 |
fmt = ImParseFormatFindStart(fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3559 |
if (fmt[0] != '%') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3560 |
return default_precision; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3561 |
fmt++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3562 |
while (*fmt >= '0' && *fmt <= '9') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3563 |
fmt++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3564 |
int precision = INT_MAX; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3565 |
if (*fmt == '.') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3566 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3567 |
fmt = ImAtoi<int>(fmt + 1, &precision); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3568 |
if (precision < 0 || precision > 99) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3569 |
precision = default_precision; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3570 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3571 |
if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3572 |
precision = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3573 |
if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3574 |
precision = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3575 |
return (precision == INT_MAX) ? default_precision : precision; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3576 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3577 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3578 |
// Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3579 |
// FIXME: Facilitate using this in variety of other situations. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3580 |
// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3581 |
// the expected relationship between TempInputXXX functions and LastItemData is a little fishy. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3582 |
bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3583 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3584 |
// On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3585 |
// We clear ActiveID on the first frame to allow the InputText() taking it back. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3586 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3587 |
const bool init = (g.TempInputId != id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3588 |
if (init) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3589 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3590 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3591 |
g.CurrentWindow->DC.CursorPos = bb.Min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3592 |
g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3593 |
bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3594 |
if (init) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3595 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3596 |
// First frame we started displaying the InputText widget, we expect it to take the active id. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3597 |
IM_ASSERT(g.ActiveId == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3598 |
g.TempInputId = g.ActiveId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3599 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3600 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3601 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3602 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3603 |
// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3604 |
// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3605 |
// However this may not be ideal for all uses, as some user code may break on out of bound values. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3606 |
bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3607 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3608 |
// FIXME: May need to clarify display behavior if format doesn't contain %. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3609 |
// "%d" -> "%d" / "There are %d items" -> "%d" / "items" -> "%d" (fallback). Also see #6405 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3610 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3611 |
const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3612 |
char fmt_buf[32]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3613 |
char data_buf[32]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3614 |
format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3615 |
if (format[0] == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3616 |
format = type_info->PrintFmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3617 |
DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3618 |
ImStrTrimBlanks(data_buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3619 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3620 |
ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3621 |
g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3622 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3623 |
if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3624 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3625 |
// Backup old value |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3626 |
size_t data_type_size = type_info->Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3627 |
ImGuiDataTypeStorage data_backup; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3628 |
memcpy(&data_backup, p_data, data_type_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3629 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3630 |
// Apply new value (or operations) then clamp |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3631 |
DataTypeApplyFromText(data_buf, data_type, p_data, format, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3632 |
if (p_clamp_min || p_clamp_max) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3633 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3634 |
if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3635 |
ImSwap(p_clamp_min, p_clamp_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3636 |
DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3637 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3638 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3639 |
// Only mark as edited if new value is different |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3640 |
g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3641 |
value_changed = memcmp(&data_backup, p_data, data_type_size) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3642 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3643 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3644 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3645 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3646 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3647 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3648 |
void ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3649 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3650 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3651 |
g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasRefVal; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3652 |
memcpy(&g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3653 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3654 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3655 |
// Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3656 |
// Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3657 |
bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3658 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3659 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3660 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3661 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3662 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3663 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3664 |
ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3665 |
IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3666 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3667 |
if (format == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3668 |
format = DataTypeGetInfo(data_type)->PrintFmt; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3669 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3670 |
void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3671 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3672 |
char buf[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3673 |
if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3674 |
buf[0] = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3675 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3676 |
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3677 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3678 |
// Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3679 |
// We call MarkItemEdited() ourselves by comparing the actual data rather than the string. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3680 |
g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3681 |
flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3682 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3683 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3684 |
if (p_step == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3685 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3686 |
if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3687 |
value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3688 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3689 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3690 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3691 |
const float button_size = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3692 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3693 |
BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3694 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3695 |
SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3696 |
if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3697 |
value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3698 |
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3699 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3700 |
// Step buttons |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3701 |
const ImVec2 backup_frame_padding = style.FramePadding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3702 |
style.FramePadding.x = style.FramePadding.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3703 |
if (flags & ImGuiInputTextFlags_ReadOnly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3704 |
BeginDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3705 |
PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3706 |
SameLine(0, style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3707 |
if (ButtonEx("-", ImVec2(button_size, button_size))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3708 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3709 |
DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3710 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3711 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3712 |
SameLine(0, style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3713 |
if (ButtonEx("+", ImVec2(button_size, button_size))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3714 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3715 |
DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3716 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3717 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3718 |
PopItemFlag(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3719 |
if (flags & ImGuiInputTextFlags_ReadOnly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3720 |
EndDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3721 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3722 |
const char* label_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3723 |
if (label != label_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3724 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3725 |
SameLine(0, style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3726 |
TextEx(label, label_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3727 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3728 |
style.FramePadding = backup_frame_padding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3729 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3730 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3731 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3732 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3733 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3734 |
g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3735 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3736 |
MarkItemEdited(g.LastItemData.ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3737 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3738 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3739 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3740 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3741 |
bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3742 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3743 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3744 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3745 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3746 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3747 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3748 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3749 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3750 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3751 |
PushMultiItemsWidths(components, CalcItemWidth()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3752 |
size_t type_size = GDataTypeInfo[data_type].Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3753 |
for (int i = 0; i < components; i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3754 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3755 |
PushID(i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3756 |
if (i > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3757 |
SameLine(0, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3758 |
value_changed |= InputScalar("", data_type, p_data, p_step, p_step_fast, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3759 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3760 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3761 |
p_data = (void*)((char*)p_data + type_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3762 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3763 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3764 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3765 |
const char* label_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3766 |
if (label != label_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3767 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3768 |
SameLine(0.0f, g.Style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3769 |
TextEx(label, label_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3770 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3771 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3772 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3773 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3774 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3775 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3776 |
bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3777 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3778 |
return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3779 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3780 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3781 |
bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3782 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3783 |
return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3784 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3785 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3786 |
bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3787 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3788 |
return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3789 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3790 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3791 |
bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3792 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3793 |
return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3794 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3795 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3796 |
bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3797 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3798 |
// Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3799 |
const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3800 |
return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL), format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3801 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3802 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3803 |
bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3804 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3805 |
return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3806 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3807 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3808 |
bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3809 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3810 |
return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3811 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3812 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3813 |
bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3814 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3815 |
return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3816 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3817 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3818 |
bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3819 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3820 |
return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3821 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3822 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3823 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3824 |
// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3825 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3826 |
// - imstb_textedit.h include |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3827 |
// - InputText() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3828 |
// - InputTextWithHint() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3829 |
// - InputTextMultiline() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3830 |
// - InputTextGetCharInfo() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3831 |
// - InputTextReindexLines() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3832 |
// - InputTextReindexLinesRange() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3833 |
// - InputTextEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3834 |
// - DebugNodeInputTextState() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3835 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3836 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3837 |
namespace ImStb |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3838 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3839 |
#include "imstb_textedit.h" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3840 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3841 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3842 |
bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3843 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3844 |
IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3845 |
return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3846 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3847 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3848 |
bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3849 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3850 |
return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3851 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3852 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3853 |
bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3854 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3855 |
IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() or InputTextEx() manually if you need multi-line + hint. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3856 |
return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3857 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3858 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3859 |
// This is only used in the path where the multiline widget is inactivate. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3860 |
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3861 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3862 |
int line_count = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3863 |
const char* s = text_begin; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3864 |
while (true) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3865 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3866 |
const char* s_eol = strchr(s, '\n'); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3867 |
line_count++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3868 |
if (s_eol == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3869 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3870 |
s = s + strlen(s); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3871 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3872 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3873 |
s = s_eol + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3874 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3875 |
*out_text_end = s; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3876 |
return line_count; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3877 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3878 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3879 |
// FIXME: Ideally we'd share code with ImFont::CalcTextSizeA() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3880 |
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3881 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3882 |
ImGuiContext& g = *ctx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3883 |
ImFont* font = g.Font; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3884 |
const float line_height = g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3885 |
const float scale = line_height / font->FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3886 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3887 |
ImVec2 text_size = ImVec2(0, 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3888 |
float line_width = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3889 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3890 |
const char* s = text_begin; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3891 |
while (s < text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3892 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3893 |
unsigned int c = (unsigned int)*s; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3894 |
if (c < 0x80) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3895 |
s += 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3896 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3897 |
s += ImTextCharFromUtf8(&c, s, text_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3898 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3899 |
if (c == '\n') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3900 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3901 |
text_size.x = ImMax(text_size.x, line_width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3902 |
text_size.y += line_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3903 |
line_width = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3904 |
if (stop_on_new_line) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3905 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3906 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3907 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3908 |
if (c == '\r') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3909 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3910 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3911 |
const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3912 |
line_width += char_width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3913 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3914 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3915 |
if (text_size.x < line_width) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3916 |
text_size.x = line_width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3917 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3918 |
if (out_offset) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3919 |
*out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3920 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3921 |
if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3922 |
text_size.y += line_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3923 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3924 |
if (remaining) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3925 |
*remaining = s; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3926 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3927 |
return text_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3928 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3929 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3930 |
// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3931 |
// With our UTF-8 use of stb_textedit: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3932 |
// - STB_TEXTEDIT_GETCHAR is nothing more than a a "GETBYTE". It's only used to compare to ascii or to copy blocks of text so we are fine. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3933 |
// - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3934 |
// - ...but we don't use that feature. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3935 |
namespace ImStb |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3936 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3937 |
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenA; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3938 |
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->CurLenA); return obj->TextA[idx]; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3939 |
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextA.Size); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3940 |
static char STB_TEXTEDIT_NEWLINE = '\n'; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3941 |
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3942 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3943 |
const char* text = obj->TextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3944 |
const char* text_remaining = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3945 |
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->CurLenA, &text_remaining, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3946 |
r->x0 = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3947 |
r->x1 = size.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3948 |
r->baseline_y_delta = size.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3949 |
r->ymin = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3950 |
r->ymax = size.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3951 |
r->num_chars = (int)(text_remaining - (text + line_start_idx)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3952 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3953 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3954 |
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3955 |
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3956 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3957 |
static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3958 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3959 |
if (idx >= obj->CurLenA) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3960 |
return obj->CurLenA + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3961 |
unsigned int c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3962 |
return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3963 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3964 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3965 |
static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3966 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3967 |
if (idx <= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3968 |
return -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3969 |
const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3970 |
return (int)(p - obj->TextA.Data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3971 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3972 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3973 |
static bool ImCharIsSeparatorW(unsigned int c) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3974 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3975 |
static const unsigned int separator_list[] = |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3976 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3977 |
',', 0x3001, '.', 0x3002, ';', 0xFF1B, '(', 0xFF08, ')', 0xFF09, '{', 0xFF5B, '}', 0xFF5D, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3978 |
'[', 0x300C, ']', 0x300D, '|', 0xFF5C, '!', 0xFF01, '\\', 0xFFE5, '/', 0x30FB, 0xFF0F, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3979 |
'\n', '\r', |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3980 |
}; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3981 |
for (unsigned int separator : separator_list) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3982 |
if (c == separator) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3983 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3984 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3985 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3986 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3987 |
static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3988 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3989 |
// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3990 |
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3991 |
return 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3992 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3993 |
const char* curr_p = obj->TextA.Data + idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3994 |
const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3995 |
unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextA.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3996 |
unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextA.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3997 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3998 |
bool prev_white = ImCharIsBlankW(prev_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
3999 |
bool prev_separ = ImCharIsSeparatorW(prev_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4000 |
bool curr_white = ImCharIsBlankW(curr_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4001 |
bool curr_separ = ImCharIsSeparatorW(curr_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4002 |
return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4003 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4004 |
static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4005 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4006 |
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4007 |
return 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4008 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4009 |
const char* curr_p = obj->TextA.Data + idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4010 |
const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4011 |
unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextA.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4012 |
unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextA.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4013 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4014 |
bool prev_white = ImCharIsBlankW(prev_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4015 |
bool prev_separ = ImCharIsSeparatorW(prev_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4016 |
bool curr_white = ImCharIsBlankW(curr_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4017 |
bool curr_separ = ImCharIsSeparatorW(curr_c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4018 |
return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4019 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4020 |
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4021 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4022 |
idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4023 |
while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4024 |
idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4025 |
return idx < 0 ? 0 : idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4026 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4027 |
static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4028 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4029 |
int len = obj->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4030 |
idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4031 |
while (idx < len && !is_word_boundary_from_left(obj, idx)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4032 |
idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4033 |
return idx > len ? len : idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4034 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4035 |
static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4036 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4037 |
idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4038 |
int len = obj->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4039 |
while (idx < len && !is_word_boundary_from_right(obj, idx)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4040 |
idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4041 |
return idx > len ? len : idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4042 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4043 |
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4044 |
#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4045 |
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4046 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4047 |
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4048 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4049 |
char* dst = obj->TextA.Data + pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4050 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4051 |
obj->Edited = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4052 |
obj->CurLenA -= n; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4053 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4054 |
// Offset remaining text (FIXME-OPT: Use memmove) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4055 |
const char* src = obj->TextA.Data + pos + n; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4056 |
while (char c = *src++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4057 |
*dst++ = c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4058 |
*dst = '\0'; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4059 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4060 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4061 |
static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4062 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4063 |
const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4064 |
const int text_len = obj->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4065 |
IM_ASSERT(pos <= text_len); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4066 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4067 |
if (!is_resizable && (new_text_len + obj->CurLenA + 1 > obj->BufCapacityA)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4068 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4069 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4070 |
// Grow internal buffer if needed |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4071 |
if (new_text_len + text_len + 1 > obj->TextA.Size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4072 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4073 |
if (!is_resizable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4074 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4075 |
obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4076 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4077 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4078 |
char* text = obj->TextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4079 |
if (pos != text_len) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4080 |
memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4081 |
memcpy(text + pos, new_text, (size_t)new_text_len); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4082 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4083 |
obj->Edited = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4084 |
obj->CurLenA += new_text_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4085 |
obj->TextA[obj->CurLenA] = '\0'; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4086 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4087 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4088 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4089 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4090 |
// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4091 |
#define STB_TEXTEDIT_K_LEFT 0x200000 // keyboard input to move cursor left |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4092 |
#define STB_TEXTEDIT_K_RIGHT 0x200001 // keyboard input to move cursor right |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4093 |
#define STB_TEXTEDIT_K_UP 0x200002 // keyboard input to move cursor up |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4094 |
#define STB_TEXTEDIT_K_DOWN 0x200003 // keyboard input to move cursor down |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4095 |
#define STB_TEXTEDIT_K_LINESTART 0x200004 // keyboard input to move cursor to start of line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4096 |
#define STB_TEXTEDIT_K_LINEEND 0x200005 // keyboard input to move cursor to end of line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4097 |
#define STB_TEXTEDIT_K_TEXTSTART 0x200006 // keyboard input to move cursor to start of text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4098 |
#define STB_TEXTEDIT_K_TEXTEND 0x200007 // keyboard input to move cursor to end of text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4099 |
#define STB_TEXTEDIT_K_DELETE 0x200008 // keyboard input to delete selection or character under cursor |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4100 |
#define STB_TEXTEDIT_K_BACKSPACE 0x200009 // keyboard input to delete selection or character left of cursor |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4101 |
#define STB_TEXTEDIT_K_UNDO 0x20000A // keyboard input to perform undo |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4102 |
#define STB_TEXTEDIT_K_REDO 0x20000B // keyboard input to perform redo |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4103 |
#define STB_TEXTEDIT_K_WORDLEFT 0x20000C // keyboard input to move cursor left one word |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4104 |
#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4105 |
#define STB_TEXTEDIT_K_PGUP 0x20000E // keyboard input to move cursor up a page |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4106 |
#define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4107 |
#define STB_TEXTEDIT_K_SHIFT 0x400000 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4108 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4109 |
#define IMSTB_TEXTEDIT_IMPLEMENTATION |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4110 |
#define IMSTB_TEXTEDIT_memmove memmove |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4111 |
#include "imstb_textedit.h" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4112 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4113 |
// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4114 |
// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4115 |
static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4116 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4117 |
stb_text_makeundo_replace(str, state, 0, str->CurLenA, text_len); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4118 |
ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenA); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4119 |
state->cursor = state->select_start = state->select_end = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4120 |
if (text_len <= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4121 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4122 |
if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4123 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4124 |
state->cursor = state->select_start = state->select_end = text_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4125 |
state->has_preferred_x = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4126 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4127 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4128 |
IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4129 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4130 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4131 |
} // namespace ImStb |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4132 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4133 |
// We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4134 |
ImGuiInputTextState::ImGuiInputTextState() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4135 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4136 |
memset(this, 0, sizeof(*this)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4137 |
Stb = IM_NEW(ImStbTexteditState); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4138 |
memset(Stb, 0, sizeof(*Stb)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4139 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4140 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4141 |
ImGuiInputTextState::~ImGuiInputTextState() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4142 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4143 |
IM_DELETE(Stb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4144 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4145 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4146 |
void ImGuiInputTextState::OnKeyPressed(int key) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4147 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4148 |
stb_textedit_key(this, Stb, key); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4149 |
CursorFollow = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4150 |
CursorAnimReset(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4151 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4152 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4153 |
void ImGuiInputTextState::OnCharPressed(unsigned int c) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4154 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4155 |
// Convert the key to a UTF8 byte sequence. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4156 |
// The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4157 |
char utf8[5]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4158 |
ImTextCharToUtf8(utf8, c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4159 |
stb_textedit_text(this, Stb, utf8, (int)strlen(utf8)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4160 |
CursorFollow = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4161 |
CursorAnimReset(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4162 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4163 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4164 |
// Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4165 |
void ImGuiInputTextState::CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4166 |
void ImGuiInputTextState::CursorClamp() { Stb->cursor = ImMin(Stb->cursor, CurLenA); Stb->select_start = ImMin(Stb->select_start, CurLenA); Stb->select_end = ImMin(Stb->select_end, CurLenA); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4167 |
bool ImGuiInputTextState::HasSelection() const { return Stb->select_start != Stb->select_end; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4168 |
void ImGuiInputTextState::ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4169 |
int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4170 |
int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4171 |
int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4172 |
void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenA; Stb->has_preferred_x = 0; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4173 |
void ImGuiInputTextState::ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4174 |
void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4175 |
void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4176 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4177 |
ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4178 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4179 |
memset(this, 0, sizeof(*this)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4180 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4181 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4182 |
// Public API to manipulate UTF-8 text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4183 |
// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4184 |
// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4185 |
void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4186 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4187 |
IM_ASSERT(pos + bytes_count <= BufTextLen); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4188 |
char* dst = Buf + pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4189 |
const char* src = Buf + pos + bytes_count; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4190 |
while (char c = *src++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4191 |
*dst++ = c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4192 |
*dst = '\0'; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4193 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4194 |
if (CursorPos >= pos + bytes_count) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4195 |
CursorPos -= bytes_count; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4196 |
else if (CursorPos >= pos) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4197 |
CursorPos = pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4198 |
SelectionStart = SelectionEnd = CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4199 |
BufDirty = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4200 |
BufTextLen -= bytes_count; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4201 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4202 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4203 |
void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4204 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4205 |
// Accept null ranges |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4206 |
if (new_text == new_text_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4207 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4208 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4209 |
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4210 |
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4211 |
if (new_text_len + BufTextLen >= BufSize) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4212 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4213 |
if (!is_resizable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4214 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4215 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4216 |
// Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4217 |
ImGuiContext& g = *Ctx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4218 |
ImGuiInputTextState* edit_state = &g.InputTextState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4219 |
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4220 |
IM_ASSERT(Buf == edit_state->TextA.Data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4221 |
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4222 |
edit_state->TextA.reserve(new_buf_size + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4223 |
Buf = edit_state->TextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4224 |
BufSize = edit_state->BufCapacityA = new_buf_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4225 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4226 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4227 |
if (BufTextLen != pos) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4228 |
memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4229 |
memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4230 |
Buf[BufTextLen + new_text_len] = '\0'; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4231 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4232 |
if (CursorPos >= pos) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4233 |
CursorPos += new_text_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4234 |
SelectionStart = SelectionEnd = CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4235 |
BufDirty = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4236 |
BufTextLen += new_text_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4237 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4238 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4239 |
// Return false to discard a character. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4240 |
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4241 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4242 |
unsigned int c = *p_char; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4243 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4244 |
// Filter non-printable (NB: isprint is unreliable! see #2467) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4245 |
bool apply_named_filters = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4246 |
if (c < 0x20) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4247 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4248 |
bool pass = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4249 |
pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4250 |
pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4251 |
if (!pass) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4252 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4253 |
apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4254 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4255 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4256 |
if (input_source_is_clipboard == false) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4257 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4258 |
// We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4259 |
if (c == 127) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4260 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4261 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4262 |
// Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4263 |
if (c >= 0xE000 && c <= 0xF8FF) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4264 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4265 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4266 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4267 |
// Filter Unicode ranges we are not handling in this build |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4268 |
if (c > IM_UNICODE_CODEPOINT_MAX) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4269 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4270 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4271 |
// Generic named filters |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4272 |
if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4273 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4274 |
// The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4275 |
// The standard mandate that programs starts in the "C" locale where the decimal point is '.'. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4276 |
// We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4277 |
// Change the default decimal_point with: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4278 |
// ImGui::GetPlatformIO()->Platform_LocaleDecimalPoint = *localeconv()->decimal_point; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4279 |
// Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4280 |
ImGuiContext& g = *ctx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4281 |
const unsigned c_decimal_point = (unsigned int)g.PlatformIO.Platform_LocaleDecimalPoint; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4282 |
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4283 |
if (c == '.' || c == ',') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4284 |
c = c_decimal_point; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4285 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4286 |
// Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4287 |
// While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4288 |
// scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4289 |
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4290 |
if (c >= 0xFF01 && c <= 0xFF5E) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4291 |
c = c - 0xFF01 + 0x21; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4292 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4293 |
// Allow 0-9 . - + * / |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4294 |
if (flags & ImGuiInputTextFlags_CharsDecimal) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4295 |
if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/')) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4296 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4297 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4298 |
// Allow 0-9 . - + * / e E |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4299 |
if (flags & ImGuiInputTextFlags_CharsScientific) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4300 |
if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4301 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4302 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4303 |
// Allow 0-9 a-F A-F |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4304 |
if (flags & ImGuiInputTextFlags_CharsHexadecimal) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4305 |
if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4306 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4307 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4308 |
// Turn a-z into A-Z |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4309 |
if (flags & ImGuiInputTextFlags_CharsUppercase) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4310 |
if (c >= 'a' && c <= 'z') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4311 |
c += (unsigned int)('A' - 'a'); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4312 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4313 |
if (flags & ImGuiInputTextFlags_CharsNoBlank) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4314 |
if (ImCharIsBlankW(c)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4315 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4316 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4317 |
*p_char = c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4318 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4319 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4320 |
// Custom callback filter |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4321 |
if (flags & ImGuiInputTextFlags_CallbackCharFilter) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4322 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4323 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4324 |
ImGuiInputTextCallbackData callback_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4325 |
callback_data.Ctx = &g; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4326 |
callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4327 |
callback_data.EventChar = (ImWchar)c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4328 |
callback_data.Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4329 |
callback_data.UserData = user_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4330 |
if (callback(&callback_data) != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4331 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4332 |
*p_char = callback_data.EventChar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4333 |
if (!callback_data.EventChar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4334 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4335 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4336 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4337 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4338 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4339 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4340 |
// Find the shortest single replacement we can make to get the new text from the old text. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4341 |
// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4342 |
// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4343 |
static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4344 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4345 |
const char* old_buf = state->CallbackTextBackup.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4346 |
const int old_length = state->CallbackTextBackup.Size - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4347 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4348 |
const int shorter_length = ImMin(old_length, new_length_a); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4349 |
int first_diff; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4350 |
for (first_diff = 0; first_diff < shorter_length; first_diff++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4351 |
if (old_buf[first_diff] != new_buf_a[first_diff]) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4352 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4353 |
if (first_diff == old_length && first_diff == new_length_a) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4354 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4355 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4356 |
int old_last_diff = old_length - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4357 |
int new_last_diff = new_length_a - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4358 |
for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4359 |
if (old_buf[old_last_diff] != new_buf_a[new_last_diff]) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4360 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4361 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4362 |
const int insert_len = new_last_diff - first_diff + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4363 |
const int delete_len = old_last_diff - first_diff + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4364 |
if (insert_len > 0 || delete_len > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4365 |
if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4366 |
for (int i = 0; i < delete_len; i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4367 |
p[i] = old_buf[first_diff + i]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4368 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4369 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4370 |
// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4371 |
// we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4372 |
// It would be more desirable that we discourage users from taking advantage of the "user not retaining data" trick, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4373 |
// but that more likely be attractive when we do have _NoLiveEdit flag available. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4374 |
void ImGui::InputTextDeactivateHook(ImGuiID id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4375 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4376 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4377 |
ImGuiInputTextState* state = &g.InputTextState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4378 |
if (id == 0 || state->ID != id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4379 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4380 |
g.InputTextDeactivatedState.ID = state->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4381 |
if (state->Flags & ImGuiInputTextFlags_ReadOnly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4382 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4383 |
g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4384 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4385 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4386 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4387 |
IM_ASSERT(state->TextA.Data != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4388 |
g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4389 |
memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->CurLenA + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4390 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4391 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4392 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4393 |
// Edit a string of text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4394 |
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4395 |
// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4396 |
// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4397 |
// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4398 |
// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4399 |
// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4400 |
// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4401 |
bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4402 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4403 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4404 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4405 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4406 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4407 |
IM_ASSERT(buf != NULL && buf_size >= 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4408 |
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4409 |
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4410 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4411 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4412 |
ImGuiIO& io = g.IO; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4413 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4414 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4415 |
const bool RENDER_SELECTION_WHEN_INACTIVE = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4416 |
const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4417 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4418 |
if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4419 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4420 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4421 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4422 |
const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4423 |
const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4424 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4425 |
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4426 |
const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4427 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4428 |
ImGuiWindow* draw_window = window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4429 |
ImVec2 inner_size = frame_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4430 |
ImGuiLastItemData item_data_backup; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4431 |
if (is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4432 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4433 |
ImVec2 backup_pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4434 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4435 |
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4436 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4437 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4438 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4439 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4440 |
item_data_backup = g.LastItemData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4441 |
window->DC.CursorPos = backup_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4442 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4443 |
// Prevent NavActivation from Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4444 |
if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && (flags & ImGuiInputTextFlags_AllowTabInput)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4445 |
g.NavActivateId = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4446 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4447 |
// Prevent NavActivate reactivating in BeginChild() when we are already active. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4448 |
const ImGuiID backup_activate_id = g.NavActivateId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4449 |
if (g.ActiveId == id) // Prevent reactivation |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4450 |
g.NavActivateId = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4451 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4452 |
// We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4453 |
PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4454 |
PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4455 |
PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4456 |
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4457 |
bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4458 |
g.NavActivateId = backup_activate_id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4459 |
PopStyleVar(3); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4460 |
PopStyleColor(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4461 |
if (!child_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4462 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4463 |
EndChild(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4464 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4465 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4466 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4467 |
draw_window = g.CurrentWindow; // Child window |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4468 |
draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4469 |
draw_window->DC.CursorPos += style.FramePadding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4470 |
inner_size.x -= draw_window->ScrollbarSizes.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4471 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4472 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4473 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4474 |
// Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4475 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4476 |
if (!(flags & ImGuiInputTextFlags_MergedItem)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4477 |
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4478 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4479 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4480 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4481 |
// Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4482 |
bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4483 |
if (hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4484 |
SetMouseCursor(ImGuiMouseCursor_TextInput); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4485 |
if (hovered && g.NavHighlightItemUnderNav) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4486 |
hovered = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4487 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4488 |
// We are only allowed to access the state if we are already the active widget. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4489 |
ImGuiInputTextState* state = GetInputTextState(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4490 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4491 |
if (g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4492 |
flags |= ImGuiInputTextFlags_ReadOnly; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4493 |
const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4494 |
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4495 |
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4496 |
const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4497 |
if (is_resizable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4498 |
IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4499 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4500 |
const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4501 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4502 |
const bool user_clicked = hovered && io.MouseClicked[0]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4503 |
const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4504 |
const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4505 |
bool clear_active_id = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4506 |
bool select_all = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4507 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4508 |
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4509 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4510 |
const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4511 |
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4512 |
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4513 |
const bool init_state = (init_make_active || user_scroll_active); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4514 |
if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4515 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4516 |
// Access state even if we don't own it yet. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4517 |
state = &g.InputTextState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4518 |
state->CursorAnimReset(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4519 |
state->ReloadUserBuf = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4520 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4521 |
// Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4522 |
InputTextDeactivateHook(state->ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4523 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4524 |
// From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4525 |
const int buf_len = (int)strlen(buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4526 |
if (!init_reload_from_user_buf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4527 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4528 |
// Take a copy of the initial buffer value. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4529 |
state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4530 |
memcpy(state->InitialTextA.Data, buf, buf_len + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4531 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4532 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4533 |
// Preserve cursor position and undo/redo stack if we come back to same widget |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4534 |
// FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4535 |
bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4536 |
if (recycle_state && (state->CurLenA != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4537 |
recycle_state = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4538 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4539 |
// Start edition |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4540 |
state->ID = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4541 |
state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4542 |
state->CurLenA = (int)strlen(buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4543 |
memcpy(state->TextA.Data, buf, state->CurLenA + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4544 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4545 |
if (recycle_state) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4546 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4547 |
// Recycle existing cursor/selection/undo stack but clamp position |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4548 |
// Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4549 |
state->CursorClamp(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4550 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4551 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4552 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4553 |
state->Scroll = ImVec2(0.0f, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4554 |
stb_textedit_initialize_state(state->Stb, !is_multiline); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4555 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4556 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4557 |
if (init_reload_from_user_buf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4558 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4559 |
state->Stb->select_start = state->ReloadSelectionStart; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4560 |
state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4561 |
state->CursorClamp(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4562 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4563 |
else if (!is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4564 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4565 |
if (flags & ImGuiInputTextFlags_AutoSelectAll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4566 |
select_all = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4567 |
if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4568 |
select_all = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4569 |
if (user_clicked && io.KeyCtrl) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4570 |
select_all = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4571 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4572 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4573 |
if (flags & ImGuiInputTextFlags_AlwaysOverwrite) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4574 |
state->Stb->insert_mode = 1; // stb field name is indeed incorrect (see #2863) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4575 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4576 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4577 |
const bool is_osx = io.ConfigMacOSXBehaviors; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4578 |
if (g.ActiveId != id && init_make_active) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4579 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4580 |
IM_ASSERT(state && state->ID == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4581 |
SetActiveID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4582 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4583 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4584 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4585 |
if (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4586 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4587 |
// Declare some inputs, the other are registered and polled via Shortcut() routing system. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4588 |
// FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combinaison into individual shortcuts. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4589 |
const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4590 |
for (ImGuiKey key : always_owned_keys) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4591 |
SetKeyOwner(key, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4592 |
if (user_clicked) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4593 |
SetKeyOwner(ImGuiKey_MouseLeft, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4594 |
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4595 |
if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4596 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4597 |
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4598 |
SetKeyOwner(ImGuiKey_UpArrow, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4599 |
SetKeyOwner(ImGuiKey_DownArrow, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4600 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4601 |
if (is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4602 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4603 |
SetKeyOwner(ImGuiKey_PageUp, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4604 |
SetKeyOwner(ImGuiKey_PageDown, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4605 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4606 |
// FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4607 |
if (is_osx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4608 |
SetKeyOwner(ImGuiMod_Alt, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4609 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4610 |
// Expose scroll in a manner that is agnostic to us using a child window |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4611 |
if (is_multiline && state != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4612 |
state->Scroll.y = draw_window->Scroll.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4613 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4614 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4615 |
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4616 |
if (g.ActiveId == id && state == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4617 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4618 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4619 |
// Release focus when we click outside |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4620 |
if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4621 |
clear_active_id = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4622 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4623 |
// Lock the decision of whether we are going to take the path displaying the cursor or selection |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4624 |
bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4625 |
bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4626 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4627 |
bool validated = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4628 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4629 |
// Select the buffer to render. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4630 |
const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4631 |
const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4632 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4633 |
// Password pushes a temporary font with only a fallback glyph |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4634 |
if (is_password && !is_displaying_hint) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4635 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4636 |
const ImFontGlyph* glyph = g.Font->FindGlyph('*'); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4637 |
ImFont* password_font = &g.InputTextPasswordFont; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4638 |
password_font->FontSize = g.Font->FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4639 |
password_font->Scale = g.Font->Scale; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4640 |
password_font->Ascent = g.Font->Ascent; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4641 |
password_font->Descent = g.Font->Descent; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4642 |
password_font->ContainerAtlas = g.Font->ContainerAtlas; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4643 |
password_font->FallbackGlyph = glyph; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4644 |
password_font->FallbackAdvanceX = glyph->AdvanceX; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4645 |
IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4646 |
PushFont(password_font); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4647 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4648 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4649 |
// Process mouse inputs and character inputs |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4650 |
if (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4651 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4652 |
IM_ASSERT(state != NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4653 |
state->Edited = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4654 |
state->BufCapacityA = buf_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4655 |
state->Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4656 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4657 |
// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4658 |
// Down the line we should have a cleaner library-wide concept of Selected vs Active. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4659 |
g.ActiveIdAllowOverlap = !io.MouseDown[0]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4660 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4661 |
// Edit in progress |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4662 |
const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->Scroll.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4663 |
const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4664 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4665 |
if (select_all) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4666 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4667 |
state->SelectAll(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4668 |
state->SelectedAllMouseLock = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4669 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4670 |
else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4671 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4672 |
stb_textedit_click(state, state->Stb, mouse_x, mouse_y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4673 |
const int multiclick_count = (io.MouseClickedCount[0] - 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4674 |
if ((multiclick_count % 2) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4675 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4676 |
// Double-click: Select word |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4677 |
// We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4678 |
// FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4679 |
const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\n'; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4680 |
if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4681 |
state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4682 |
//state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4683 |
if (!STB_TEXT_HAS_SELECTION(state->Stb)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4684 |
ImStb::stb_textedit_prep_selection_at_cursor(state->Stb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4685 |
state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4686 |
state->Stb->select_end = state->Stb->cursor; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4687 |
ImStb::stb_textedit_clamp(state, state->Stb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4688 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4689 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4690 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4691 |
// Triple-click: Select line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4692 |
const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n'; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4693 |
state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4694 |
state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4695 |
state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4696 |
if (!is_eol && is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4697 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4698 |
ImSwap(state->Stb->select_start, state->Stb->select_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4699 |
state->Stb->cursor = state->Stb->select_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4700 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4701 |
state->CursorFollow = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4702 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4703 |
state->CursorAnimReset(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4704 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4705 |
else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4706 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4707 |
if (hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4708 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4709 |
if (io.KeyShift) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4710 |
stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4711 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4712 |
stb_textedit_click(state, state->Stb, mouse_x, mouse_y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4713 |
state->CursorAnimReset(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4714 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4715 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4716 |
else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4717 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4718 |
stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4719 |
state->CursorAnimReset(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4720 |
state->CursorFollow = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4721 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4722 |
if (state->SelectedAllMouseLock && !io.MouseDown[0]) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4723 |
state->SelectedAllMouseLock = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4724 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4725 |
// We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4726 |
// (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4727 |
if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4728 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4729 |
if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4730 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4731 |
unsigned int c = '\t'; // Insert TAB |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4732 |
if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4733 |
state->OnCharPressed(c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4734 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4735 |
// FIXME: Implement Shift+Tab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4736 |
/* |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4737 |
if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, ImGuiInputFlags_Repeat, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4738 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4739 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4740 |
*/ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4741 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4742 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4743 |
// Process regular text input (before we check for Return because using some IME will effectively send a Return?) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4744 |
// We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4745 |
const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4746 |
if (io.InputQueueCharacters.Size > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4747 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4748 |
if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4749 |
for (int n = 0; n < io.InputQueueCharacters.Size; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4750 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4751 |
// Insert character if they pass filtering |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4752 |
unsigned int c = (unsigned int)io.InputQueueCharacters[n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4753 |
if (c == '\t') // Skip Tab, see above. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4754 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4755 |
if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4756 |
state->OnCharPressed(c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4757 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4758 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4759 |
// Consume characters |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4760 |
io.InputQueueCharacters.resize(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4761 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4762 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4763 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4764 |
// Process other shortcuts/key-presses |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4765 |
bool revert_edit = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4766 |
if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4767 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4768 |
IM_ASSERT(state != NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4769 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4770 |
const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4771 |
state->Stb->row_count_per_page = row_count_per_page; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4772 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4773 |
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4774 |
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4775 |
const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4776 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4777 |
// Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4778 |
// Otherwise we could simply assume that we own the keys as we are active. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4779 |
const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4780 |
const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4781 |
const bool is_copy = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, 0, id)) && !is_password && (!is_multiline || state->HasSelection()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4782 |
const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4783 |
const bool is_undo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4784 |
const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || (is_osx && Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id))) && !is_readonly && is_undoable; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4785 |
const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4786 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4787 |
// We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4788 |
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4789 |
const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4790 |
const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4791 |
const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4792 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4793 |
// FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4794 |
// FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4795 |
if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4796 |
else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4797 |
else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4798 |
else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4799 |
else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4800 |
else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4801 |
else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4802 |
else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4803 |
else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4804 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4805 |
if (!state->HasSelection()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4806 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4807 |
// OSX doesn't seem to have Super+Delete to delete until end-of-line, so we don't emulate that (as opposed to Super+Backspace) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4808 |
if (is_wordmove_key_down) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4809 |
state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4810 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4811 |
state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4812 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4813 |
else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4814 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4815 |
if (!state->HasSelection()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4816 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4817 |
if (is_wordmove_key_down) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4818 |
state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4819 |
else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4820 |
state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4821 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4822 |
state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4823 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4824 |
else if (is_enter_pressed || is_gamepad_validate) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4825 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4826 |
// Determine if we turn Enter into a \n character |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4827 |
bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4828 |
if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4829 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4830 |
validated = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4831 |
if (io.ConfigInputTextEnterKeepActive && !is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4832 |
state->SelectAll(); // No need to scroll |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4833 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4834 |
clear_active_id = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4835 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4836 |
else if (!is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4837 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4838 |
unsigned int c = '\n'; // Insert new line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4839 |
if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4840 |
state->OnCharPressed(c); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4841 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4842 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4843 |
else if (is_cancel) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4844 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4845 |
if (flags & ImGuiInputTextFlags_EscapeClearsAll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4846 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4847 |
if (buf[0] != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4848 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4849 |
revert_edit = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4850 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4851 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4852 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4853 |
render_cursor = render_selection = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4854 |
clear_active_id = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4855 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4856 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4857 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4858 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4859 |
clear_active_id = revert_edit = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4860 |
render_cursor = render_selection = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4861 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4862 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4863 |
else if (is_undo || is_redo) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4864 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4865 |
state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4866 |
state->ClearSelection(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4867 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4868 |
else if (is_select_all) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4869 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4870 |
state->SelectAll(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4871 |
state->CursorFollow = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4872 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4873 |
else if (is_cut || is_copy) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4874 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4875 |
// Cut, Copy |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4876 |
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4877 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4878 |
const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4879 |
const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4880 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4881 |
char backup = state->TextA.Data[ie]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4882 |
state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4883 |
SetClipboardText(state->TextA.Data + ib); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4884 |
state->TextA.Data[ie] = backup; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4885 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4886 |
if (is_cut) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4887 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4888 |
if (!state->HasSelection()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4889 |
state->SelectAll(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4890 |
state->CursorFollow = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4891 |
stb_textedit_cut(state, state->Stb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4892 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4893 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4894 |
else if (is_paste) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4895 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4896 |
if (const char* clipboard = GetClipboardText()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4897 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4898 |
// Filter pasted buffer |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4899 |
const int clipboard_len = (int)strlen(clipboard); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4900 |
char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4901 |
int clipboard_filtered_len = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4902 |
for (const char* s = clipboard; *s != 0; ) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4903 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4904 |
unsigned int c; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4905 |
int len = ImTextCharFromUtf8(&c, s, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4906 |
s += len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4907 |
if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4908 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4909 |
memcpy(clipboard_filtered + clipboard_filtered_len, s - len, len); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4910 |
clipboard_filtered_len += len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4911 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4912 |
clipboard_filtered[clipboard_filtered_len] = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4913 |
if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4914 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4915 |
stb_textedit_paste(state, state->Stb, clipboard_filtered, clipboard_filtered_len); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4916 |
state->CursorFollow = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4917 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4918 |
MemFree(clipboard_filtered); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4919 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4920 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4921 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4922 |
// Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4923 |
render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4924 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4925 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4926 |
// Process callbacks and apply result back to user's buffer. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4927 |
const char* apply_new_text = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4928 |
int apply_new_text_length = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4929 |
if (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4930 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4931 |
IM_ASSERT(state != NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4932 |
if (revert_edit && !is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4933 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4934 |
if (flags & ImGuiInputTextFlags_EscapeClearsAll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4935 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4936 |
// Clear input |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4937 |
IM_ASSERT(buf[0] != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4938 |
apply_new_text = ""; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4939 |
apply_new_text_length = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4940 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4941 |
IMSTB_TEXTEDIT_CHARTYPE empty_string; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4942 |
stb_textedit_replace(state, state->Stb, &empty_string, 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4943 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4944 |
else if (strcmp(buf, state->InitialTextA.Data) != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4945 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4946 |
apply_new_text = state->InitialTextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4947 |
apply_new_text_length = state->InitialTextA.Size - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4948 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4949 |
// Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4950 |
// Push records into the undo stack so we can CTRL+Z the revert operation itself |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4951 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4952 |
stb_textedit_replace(state, state->Stb, state->InitialTextA.Data, state->InitialTextA.Size - 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4953 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4954 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4955 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4956 |
// When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4957 |
// before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4958 |
// If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4959 |
// This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4960 |
// (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4961 |
// unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4962 |
const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4963 |
if (apply_edit_back_to_user_buffer) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4964 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4965 |
// Apply new value immediately - copy modified buffer back |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4966 |
// Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4967 |
// FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4968 |
// FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4969 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4970 |
// User callback |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4971 |
if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4972 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4973 |
IM_ASSERT(callback != NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4974 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4975 |
// The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4976 |
ImGuiInputTextFlags event_flag = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4977 |
ImGuiKey event_key = ImGuiKey_None; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4978 |
if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4979 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4980 |
event_flag = ImGuiInputTextFlags_CallbackCompletion; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4981 |
event_key = ImGuiKey_Tab; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4982 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4983 |
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4984 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4985 |
event_flag = ImGuiInputTextFlags_CallbackHistory; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4986 |
event_key = ImGuiKey_UpArrow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4987 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4988 |
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4989 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4990 |
event_flag = ImGuiInputTextFlags_CallbackHistory; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4991 |
event_key = ImGuiKey_DownArrow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4992 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4993 |
else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4994 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4995 |
event_flag = ImGuiInputTextFlags_CallbackEdit; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4996 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4997 |
else if (flags & ImGuiInputTextFlags_CallbackAlways) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4998 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
4999 |
event_flag = ImGuiInputTextFlags_CallbackAlways; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5000 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5001 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5002 |
if (event_flag) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5003 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5004 |
ImGuiInputTextCallbackData callback_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5005 |
callback_data.Ctx = &g; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5006 |
callback_data.EventFlag = event_flag; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5007 |
callback_data.Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5008 |
callback_data.UserData = callback_user_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5009 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5010 |
// FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5011 |
state->CallbackTextBackup.resize(state->CurLenA + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5012 |
memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->CurLenA + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5013 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5014 |
char* callback_buf = is_readonly ? buf : state->TextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5015 |
callback_data.EventKey = event_key; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5016 |
callback_data.Buf = callback_buf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5017 |
callback_data.BufTextLen = state->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5018 |
callback_data.BufSize = state->BufCapacityA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5019 |
callback_data.BufDirty = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5020 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5021 |
const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5022 |
const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5023 |
const int utf8_selection_end = callback_data.SelectionEnd = state->Stb->select_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5024 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5025 |
// Call user code |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5026 |
callback(&callback_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5027 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5028 |
// Read back what user may have modified |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5029 |
callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5030 |
IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5031 |
IM_ASSERT(callback_data.BufSize == state->BufCapacityA); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5032 |
IM_ASSERT(callback_data.Flags == flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5033 |
const bool buf_dirty = callback_data.BufDirty; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5034 |
if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5035 |
if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5036 |
if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5037 |
if (buf_dirty) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5038 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5039 |
// Callback may update buffer and thus set buf_dirty even in read-only mode. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5040 |
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5041 |
InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5042 |
state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5043 |
state->TextA.Size = state->CurLenA + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5044 |
state->CursorAnimReset(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5045 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5046 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5047 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5048 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5049 |
// Will copy result string if modified |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5050 |
if (!is_readonly && strcmp(state->TextA.Data, buf) != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5051 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5052 |
apply_new_text = state->TextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5053 |
apply_new_text_length = state->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5054 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5055 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5056 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5057 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5058 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5059 |
// Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5060 |
if (g.InputTextDeactivatedState.ID == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5061 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5062 |
if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5063 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5064 |
apply_new_text = g.InputTextDeactivatedState.TextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5065 |
apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5066 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5067 |
//IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5068 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5069 |
g.InputTextDeactivatedState.ID = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5070 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5071 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5072 |
// Copy result to user buffer. This can currently only happen when (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5073 |
if (apply_new_text != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5074 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5075 |
//// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5076 |
//// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5077 |
//// without any storage on user's side. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5078 |
IM_ASSERT(apply_new_text_length >= 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5079 |
if (is_resizable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5080 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5081 |
ImGuiInputTextCallbackData callback_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5082 |
callback_data.Ctx = &g; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5083 |
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5084 |
callback_data.Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5085 |
callback_data.Buf = buf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5086 |
callback_data.BufTextLen = apply_new_text_length; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5087 |
callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5088 |
callback_data.UserData = callback_user_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5089 |
callback(&callback_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5090 |
buf = callback_data.Buf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5091 |
buf_size = callback_data.BufSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5092 |
apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5093 |
IM_ASSERT(apply_new_text_length <= buf_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5094 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5095 |
//IMGUI_DEBUG_PRINT("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5096 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5097 |
// If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5098 |
ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5099 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5100 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5101 |
// Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5102 |
// Otherwise request text input ahead for next frame. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5103 |
if (g.ActiveId == id && clear_active_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5104 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5105 |
else if (g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5106 |
g.WantTextInputNextFrame = 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5107 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5108 |
// Render frame |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5109 |
if (!is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5110 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5111 |
RenderNavCursor(frame_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5112 |
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5113 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5114 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5115 |
const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5116 |
ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5117 |
ImVec2 text_size(0.0f, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5118 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5119 |
// Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5120 |
// without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5121 |
// Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5122 |
const int buf_display_max_length = 2 * 1024 * 1024; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5123 |
const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5124 |
const char* buf_display_end = NULL; // We have specialized paths below for setting the length |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5125 |
if (is_displaying_hint) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5126 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5127 |
buf_display = hint; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5128 |
buf_display_end = hint + strlen(hint); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5129 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5130 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5131 |
// Render text. We currently only render selection when the widget is active or while scrolling. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5132 |
// FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5133 |
if (render_cursor || render_selection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5134 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5135 |
IM_ASSERT(state != NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5136 |
if (!is_displaying_hint) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5137 |
buf_display_end = buf_display + state->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5138 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5139 |
// Render text (with cursor and selection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5140 |
// This is going to be messy. We need to: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5141 |
// - Display the text (this alone can be more easily clipped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5142 |
// - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5143 |
// - Measure text height (for scrollbar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5144 |
// We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5145 |
// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5146 |
const char* text_begin = state->TextA.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5147 |
const char* text_end = text_begin + state->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5148 |
ImVec2 cursor_offset, select_start_offset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5149 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5150 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5151 |
// Find lines numbers straddling cursor and selection min position |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5152 |
int cursor_line_no = render_cursor ? -1 : -1000; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5153 |
int selmin_line_no = render_selection ? -1 : -1000; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5154 |
const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5155 |
const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5156 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5157 |
// Count lines and find line number for cursor and selection ends |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5158 |
int line_count = 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5159 |
if (is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5160 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5161 |
for (const char* s = text_begin; (s = (const char*)memchr(s, '\n', (size_t)(text_end - s))) != NULL; s++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5162 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5163 |
if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5164 |
if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5165 |
line_count++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5166 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5167 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5168 |
if (cursor_line_no == -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5169 |
cursor_line_no = line_count; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5170 |
if (selmin_line_no == -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5171 |
selmin_line_no = line_count; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5172 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5173 |
// Calculate 2d position by finding the beginning of the line and measuring distance |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5174 |
cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(cursor_ptr, text_begin), cursor_ptr).x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5175 |
cursor_offset.y = cursor_line_no * g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5176 |
if (selmin_line_no >= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5177 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5178 |
select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(selmin_ptr, text_begin), selmin_ptr).x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5179 |
select_start_offset.y = selmin_line_no * g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5180 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5181 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5182 |
// Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5183 |
if (is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5184 |
text_size = ImVec2(inner_size.x, line_count * g.FontSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5185 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5186 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5187 |
// Scroll |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5188 |
if (render_cursor && state->CursorFollow) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5189 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5190 |
// Horizontal scroll in chunks of quarter width |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5191 |
if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5192 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5193 |
const float scroll_increment_x = inner_size.x * 0.25f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5194 |
const float visible_width = inner_size.x - style.FramePadding.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5195 |
if (cursor_offset.x < state->Scroll.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5196 |
state->Scroll.x = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5197 |
else if (cursor_offset.x - visible_width >= state->Scroll.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5198 |
state->Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5199 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5200 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5201 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5202 |
state->Scroll.y = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5203 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5204 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5205 |
// Vertical scroll |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5206 |
if (is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5207 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5208 |
// Test if cursor is vertically visible |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5209 |
if (cursor_offset.y - g.FontSize < scroll_y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5210 |
scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5211 |
else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5212 |
scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5213 |
const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5214 |
scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5215 |
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5216 |
draw_window->Scroll.y = scroll_y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5217 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5218 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5219 |
state->CursorFollow = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5220 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5221 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5222 |
// Draw selection |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5223 |
const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5224 |
if (render_selection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5225 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5226 |
const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5227 |
const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5228 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5229 |
ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5230 |
float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5231 |
float bg_offy_dn = is_multiline ? 0.0f : 2.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5232 |
ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5233 |
for (const char* p = text_selected_begin; p < text_selected_end; ) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5234 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5235 |
if (rect_pos.y > clip_rect.w + g.FontSize) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5236 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5237 |
if (rect_pos.y < clip_rect.y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5238 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5239 |
p = (const char*)memchr((void*)p, '\n', text_selected_end - p); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5240 |
p = p ? p + 1 : text_selected_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5241 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5242 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5243 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5244 |
ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5245 |
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5246 |
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5247 |
rect.ClipWith(clip_rect); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5248 |
if (rect.Overlaps(clip_rect)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5249 |
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5250 |
rect_pos.x = draw_pos.x - draw_scroll.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5251 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5252 |
rect_pos.y += g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5253 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5254 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5255 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5256 |
// We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5257 |
// FIXME-OPT: Multiline could submit a smaller amount of contents to AddText() since we already iterated through it. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5258 |
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5259 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5260 |
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5261 |
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5262 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5263 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5264 |
// Draw blinking cursor |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5265 |
if (render_cursor) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5266 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5267 |
state->CursorAnim += io.DeltaTime; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5268 |
bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5269 |
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5270 |
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5271 |
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5272 |
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5273 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5274 |
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5275 |
if (!is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5276 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5277 |
g.PlatformImeData.WantVisible = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5278 |
g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5279 |
g.PlatformImeData.InputLineHeight = g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5280 |
g.PlatformImeViewport = window->Viewport->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5281 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5282 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5283 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5284 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5285 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5286 |
// Render text only (no selection, no cursor) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5287 |
if (is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5288 |
text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5289 |
else if (!is_displaying_hint && g.ActiveId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5290 |
buf_display_end = buf_display + state->CurLenA; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5291 |
else if (!is_displaying_hint) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5292 |
buf_display_end = buf_display + strlen(buf_display); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5293 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5294 |
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5295 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5296 |
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5297 |
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5298 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5299 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5300 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5301 |
if (is_password && !is_displaying_hint) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5302 |
PopFont(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5303 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5304 |
if (is_multiline) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5305 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5306 |
// For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)... |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5307 |
Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5308 |
g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5309 |
EndChild(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5310 |
item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5311 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5312 |
// ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5313 |
// FIXME: This quite messy/tricky, should attempt to get rid of the child window. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5314 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5315 |
if (g.LastItemData.ID == 0 || g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5316 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5317 |
g.LastItemData.ID = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5318 |
g.LastItemData.ItemFlags = item_data_backup.ItemFlags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5319 |
g.LastItemData.StatusFlags = item_data_backup.StatusFlags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5320 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5321 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5322 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5323 |
// Log as text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5324 |
if (g.LogEnabled && (!is_password || is_displaying_hint)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5325 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5326 |
LogSetNextTextDecoration("{", "}"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5327 |
LogRenderedText(&draw_pos, buf_display, buf_display_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5328 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5329 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5330 |
if (label_size.x > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5331 |
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5332 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5333 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5334 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5335 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5336 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5337 |
if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5338 |
return validated; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5339 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5340 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5341 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5342 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5343 |
void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5344 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5345 |
#ifndef IMGUI_DISABLE_DEBUG_TOOLS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5346 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5347 |
ImStb::STB_TexteditState* stb_state = state->Stb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5348 |
ImStb::StbUndoState* undo_state = &stb_state->undostate; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5349 |
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5350 |
DebugLocateItemOnHover(state->ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5351 |
Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5352 |
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5353 |
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5354 |
if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5355 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5356 |
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5357 |
for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5358 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5359 |
ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5360 |
const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' '; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5361 |
if (undo_rec_type == ' ') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5362 |
BeginDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5363 |
const int buf_preview_len = (undo_rec_type != ' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5364 |
const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5365 |
Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"", |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5366 |
undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5367 |
if (undo_rec_type == ' ') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5368 |
EndDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5369 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5370 |
PopStyleVar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5371 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5372 |
EndChild(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5373 |
#else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5374 |
IM_UNUSED(state); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5375 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5376 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5377 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5378 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5379 |
// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5380 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5381 |
// - ColorEdit3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5382 |
// - ColorEdit4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5383 |
// - ColorPicker3() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5384 |
// - RenderColorRectWithAlphaCheckerboard() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5385 |
// - ColorPicker4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5386 |
// - ColorButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5387 |
// - SetColorEditOptions() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5388 |
// - ColorTooltip() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5389 |
// - ColorEditOptionsPopup() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5390 |
// - ColorPickerOptionsPopup() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5391 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5392 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5393 |
bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5394 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5395 |
return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5396 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5397 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5398 |
static void ColorEditRestoreH(const float* col, float* H) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5399 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5400 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5401 |
IM_ASSERT(g.ColorEditCurrentID != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5402 |
if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5403 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5404 |
*H = g.ColorEditSavedHue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5405 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5406 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5407 |
// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5408 |
// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5409 |
static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5410 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5411 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5412 |
IM_ASSERT(g.ColorEditCurrentID != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5413 |
if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5414 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5415 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5416 |
// When S == 0, H is undefined. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5417 |
// When H == 1 it wraps around to 0. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5418 |
if (*S == 0.0f || (*H == 0.0f && g.ColorEditSavedHue == 1)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5419 |
*H = g.ColorEditSavedHue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5420 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5421 |
// When V == 0, S is undefined. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5422 |
if (*V == 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5423 |
*S = g.ColorEditSavedSat; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5424 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5425 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5426 |
// Edit colors components (each component in 0.0f..1.0f range). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5427 |
// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5428 |
// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5429 |
bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5430 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5431 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5432 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5433 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5434 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5435 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5436 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5437 |
const float square_sz = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5438 |
const char* label_display_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5439 |
float w_full = CalcItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5440 |
g.NextItemData.ClearFlags(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5441 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5442 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5443 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5444 |
const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5445 |
if (set_current_color_edit_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5446 |
g.ColorEditCurrentID = window->IDStack.back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5447 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5448 |
// If we're not showing any slider there's no point in doing any HSV conversions |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5449 |
const ImGuiColorEditFlags flags_untouched = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5450 |
if (flags & ImGuiColorEditFlags_NoInputs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5451 |
flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5452 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5453 |
// Context menu: display and modify options (before defaults are applied) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5454 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5455 |
ColorEditOptionsPopup(col, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5456 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5457 |
// Read stored options |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5458 |
if (!(flags & ImGuiColorEditFlags_DisplayMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5459 |
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5460 |
if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5461 |
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5462 |
if (!(flags & ImGuiColorEditFlags_PickerMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5463 |
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5464 |
if (!(flags & ImGuiColorEditFlags_InputMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5465 |
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5466 |
flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5467 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5468 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5469 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5470 |
const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5471 |
const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5472 |
const int components = alpha ? 4 : 3; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5473 |
const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5474 |
const float w_inputs = ImMax(w_full - w_button, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5475 |
w_full = w_inputs + w_button; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5476 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5477 |
// Convert to the formats we need |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5478 |
float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5479 |
if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5480 |
ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5481 |
else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5482 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5483 |
// Hue is lost when converting from grayscale rgb (saturation=0). Restore it. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5484 |
ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5485 |
ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5486 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5487 |
int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5488 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5489 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5490 |
bool value_changed_as_float = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5491 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5492 |
const ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5493 |
const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5494 |
window->DC.CursorPos.x = pos.x + inputs_offset_x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5495 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5496 |
if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5497 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5498 |
// RGB/HSV 0..255 Sliders |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5499 |
const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5500 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5501 |
const bool hide_prefix = (IM_TRUNC(w_items / components) <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5502 |
static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5503 |
static const char* fmt_table_int[3][4] = |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5504 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5505 |
{ "%3d", "%3d", "%3d", "%3d" }, // Short display |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5506 |
{ "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5507 |
{ "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5508 |
}; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5509 |
static const char* fmt_table_float[3][4] = |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5510 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5511 |
{ "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5512 |
{ "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5513 |
{ "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5514 |
}; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5515 |
const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5516 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5517 |
float prev_split = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5518 |
for (int n = 0; n < components; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5519 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5520 |
if (n > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5521 |
SameLine(0, style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5522 |
float next_split = IM_TRUNC(w_items * (n + 1) / components); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5523 |
SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5524 |
prev_split = next_split; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5525 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5526 |
// FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5527 |
if (flags & ImGuiColorEditFlags_Float) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5528 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5529 |
value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5530 |
value_changed_as_float |= value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5531 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5532 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5533 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5534 |
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5535 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5536 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5537 |
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5538 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5539 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5540 |
else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5541 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5542 |
// RGB Hexadecimal Input |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5543 |
char buf[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5544 |
if (alpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5545 |
ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5546 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5547 |
ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5548 |
SetNextItemWidth(w_inputs); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5549 |
if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5550 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5551 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5552 |
char* p = buf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5553 |
while (*p == '#' || ImCharIsBlankA(*p)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5554 |
p++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5555 |
i[0] = i[1] = i[2] = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5556 |
i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5557 |
int r; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5558 |
if (alpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5559 |
r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5560 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5561 |
r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5562 |
IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5563 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5564 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5565 |
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5566 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5567 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5568 |
ImGuiWindow* picker_active_window = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5569 |
if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5570 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5571 |
const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5572 |
window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5573 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5574 |
const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5575 |
if (ColorButton("##ColorButton", col_v4, flags)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5576 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5577 |
if (!(flags & ImGuiColorEditFlags_NoPicker)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5578 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5579 |
// Store current color and open a picker |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5580 |
g.ColorPickerRef = col_v4; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5581 |
OpenPopup("picker"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5582 |
SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5583 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5584 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5585 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5586 |
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5587 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5588 |
if (BeginPopup("picker")) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5589 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5590 |
if (g.CurrentWindow->BeginCount == 1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5591 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5592 |
picker_active_window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5593 |
if (label != label_display_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5594 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5595 |
TextEx(label, label_display_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5596 |
Spacing(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5597 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5598 |
ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5599 |
ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5600 |
SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5601 |
value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5602 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5603 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5604 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5605 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5606 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5607 |
if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5608 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5609 |
// Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left), |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5610 |
// but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5611 |
SameLine(0.0f, style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5612 |
window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5613 |
TextEx(label, label_display_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5614 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5615 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5616 |
// Convert back |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5617 |
if (value_changed && picker_active_window == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5618 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5619 |
if (!value_changed_as_float) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5620 |
for (int n = 0; n < 4; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5621 |
f[n] = i[n] / 255.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5622 |
if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5623 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5624 |
g.ColorEditSavedHue = f[0]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5625 |
g.ColorEditSavedSat = f[1]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5626 |
ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5627 |
g.ColorEditSavedID = g.ColorEditCurrentID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5628 |
g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5629 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5630 |
if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5631 |
ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5632 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5633 |
col[0] = f[0]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5634 |
col[1] = f[1]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5635 |
col[2] = f[2]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5636 |
if (alpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5637 |
col[3] = f[3]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5638 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5639 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5640 |
if (set_current_color_edit_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5641 |
g.ColorEditCurrentID = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5642 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5643 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5644 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5645 |
// Drag and Drop Target |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5646 |
// NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5647 |
if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5648 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5649 |
bool accepted_drag_drop = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5650 |
if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5651 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5652 |
memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 //-V1086 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5653 |
value_changed = accepted_drag_drop = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5654 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5655 |
if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5656 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5657 |
memcpy((float*)col, payload->Data, sizeof(float) * components); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5658 |
value_changed = accepted_drag_drop = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5659 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5660 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5661 |
// Drag-drop payloads are always RGB |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5662 |
if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5663 |
ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5664 |
EndDragDropTarget(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5665 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5666 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5667 |
// When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5668 |
if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5669 |
g.LastItemData.ID = g.ActiveId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5670 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5671 |
if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5672 |
MarkItemEdited(g.LastItemData.ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5673 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5674 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5675 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5676 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5677 |
bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5678 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5679 |
float col4[4] = { col[0], col[1], col[2], 1.0f }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5680 |
if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5681 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5682 |
col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5683 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5684 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5685 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5686 |
// Helper for ColorPicker4() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5687 |
static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w, float alpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5688 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5689 |
ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5690 |
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32(0,0,0,alpha8)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5691 |
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32(255,255,255,alpha8)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5692 |
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32(0,0,0,alpha8)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5693 |
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32(255,255,255,alpha8)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5694 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5695 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5696 |
// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5697 |
// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5698 |
// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5699 |
// FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5700 |
bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5701 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5702 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5703 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5704 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5705 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5706 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5707 |
ImDrawList* draw_list = window->DrawList; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5708 |
ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5709 |
ImGuiIO& io = g.IO; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5710 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5711 |
const float width = CalcItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5712 |
const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5713 |
g.NextItemData.ClearFlags(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5714 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5715 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5716 |
const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5717 |
if (set_current_color_edit_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5718 |
g.ColorEditCurrentID = window->IDStack.back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5719 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5720 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5721 |
if (!(flags & ImGuiColorEditFlags_NoSidePreview)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5722 |
flags |= ImGuiColorEditFlags_NoSmallPreview; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5723 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5724 |
// Context menu: display and store options. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5725 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5726 |
ColorPickerOptionsPopup(col, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5727 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5728 |
// Read stored options |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5729 |
if (!(flags & ImGuiColorEditFlags_PickerMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5730 |
flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5731 |
if (!(flags & ImGuiColorEditFlags_InputMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5732 |
flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5733 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5734 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5735 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5736 |
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5737 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5738 |
// Setup |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5739 |
int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5740 |
bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5741 |
ImVec2 picker_pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5742 |
float square_sz = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5743 |
float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5744 |
float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5745 |
float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5746 |
float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5747 |
float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5748 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5749 |
float backup_initial_col[4]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5750 |
memcpy(backup_initial_col, col, components * sizeof(float)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5751 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5752 |
float wheel_thickness = sv_picker_size * 0.08f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5753 |
float wheel_r_outer = sv_picker_size * 0.50f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5754 |
float wheel_r_inner = wheel_r_outer - wheel_thickness; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5755 |
ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5756 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5757 |
// Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5758 |
float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5759 |
ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5760 |
ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5761 |
ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5762 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5763 |
float H = col[0], S = col[1], V = col[2]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5764 |
float R = col[0], G = col[1], B = col[2]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5765 |
if (flags & ImGuiColorEditFlags_InputRGB) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5766 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5767 |
// Hue is lost when converting from grayscale rgb (saturation=0). Restore it. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5768 |
ColorConvertRGBtoHSV(R, G, B, H, S, V); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5769 |
ColorEditRestoreHS(col, &H, &S, &V); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5770 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5771 |
else if (flags & ImGuiColorEditFlags_InputHSV) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5772 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5773 |
ColorConvertHSVtoRGB(H, S, V, R, G, B); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5774 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5775 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5776 |
bool value_changed = false, value_changed_h = false, value_changed_sv = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5777 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5778 |
PushItemFlag(ImGuiItemFlags_NoNav, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5779 |
if (flags & ImGuiColorEditFlags_PickerHueWheel) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5780 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5781 |
// Hue wheel + SV triangle logic |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5782 |
InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5783 |
if (IsItemActive() && !is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5784 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5785 |
ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5786 |
ImVec2 current_off = g.IO.MousePos - wheel_center; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5787 |
float initial_dist2 = ImLengthSqr(initial_off); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5788 |
if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5789 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5790 |
// Interactive with Hue wheel |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5791 |
H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5792 |
if (H < 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5793 |
H += 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5794 |
value_changed = value_changed_h = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5795 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5796 |
float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5797 |
float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5798 |
if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5799 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5800 |
// Interacting with SV triangle |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5801 |
ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5802 |
if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5803 |
current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5804 |
float uu, vv, ww; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5805 |
ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5806 |
V = ImClamp(1.0f - vv, 0.0001f, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5807 |
S = ImClamp(uu / V, 0.0001f, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5808 |
value_changed = value_changed_sv = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5809 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5810 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5811 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5812 |
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5813 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5814 |
else if (flags & ImGuiColorEditFlags_PickerHueBar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5815 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5816 |
// SV rectangle logic |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5817 |
InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5818 |
if (IsItemActive() && !is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5819 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5820 |
S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5821 |
V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5822 |
ColorEditRestoreH(col, &H); // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5823 |
value_changed = value_changed_sv = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5824 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5825 |
if (!(flags & ImGuiColorEditFlags_NoOptions)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5826 |
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5827 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5828 |
// Hue bar logic |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5829 |
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5830 |
InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5831 |
if (IsItemActive() && !is_readonly) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5832 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5833 |
H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5834 |
value_changed = value_changed_h = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5835 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5836 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5837 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5838 |
// Alpha bar logic |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5839 |
if (alpha_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5840 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5841 |
SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5842 |
InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5843 |
if (IsItemActive()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5844 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5845 |
col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5846 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5847 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5848 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5849 |
PopItemFlag(); // ImGuiItemFlags_NoNav |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5850 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5851 |
if (!(flags & ImGuiColorEditFlags_NoSidePreview)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5852 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5853 |
SameLine(0, style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5854 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5855 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5856 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5857 |
if (!(flags & ImGuiColorEditFlags_NoLabel)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5858 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5859 |
const char* label_display_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5860 |
if (label != label_display_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5861 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5862 |
if ((flags & ImGuiColorEditFlags_NoSidePreview)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5863 |
SameLine(0, style.ItemInnerSpacing.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5864 |
TextEx(label, label_display_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5865 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5866 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5867 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5868 |
if (!(flags & ImGuiColorEditFlags_NoSidePreview)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5869 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5870 |
PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5871 |
ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5872 |
if ((flags & ImGuiColorEditFlags_NoLabel)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5873 |
Text("Current"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5874 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5875 |
ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5876 |
ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5877 |
if (ref_col != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5878 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5879 |
Text("Original"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5880 |
ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5881 |
if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5882 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5883 |
memcpy(col, ref_col, components * sizeof(float)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5884 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5885 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5886 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5887 |
PopItemFlag(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5888 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5889 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5890 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5891 |
// Convert back color to RGB |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5892 |
if (value_changed_h || value_changed_sv) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5893 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5894 |
if (flags & ImGuiColorEditFlags_InputRGB) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5895 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5896 |
ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5897 |
g.ColorEditSavedHue = H; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5898 |
g.ColorEditSavedSat = S; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5899 |
g.ColorEditSavedID = g.ColorEditCurrentID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5900 |
g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5901 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5902 |
else if (flags & ImGuiColorEditFlags_InputHSV) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5903 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5904 |
col[0] = H; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5905 |
col[1] = S; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5906 |
col[2] = V; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5907 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5908 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5909 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5910 |
// R,G,B and H,S,V slider color editor |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5911 |
bool value_changed_fix_hue_wrap = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5912 |
if ((flags & ImGuiColorEditFlags_NoInputs) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5913 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5914 |
PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5915 |
ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5916 |
ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5917 |
if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5918 |
if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5919 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5920 |
// FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5921 |
// For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5922 |
value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5923 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5924 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5925 |
if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5926 |
value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5927 |
if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5928 |
value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5929 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5930 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5931 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5932 |
// Try to cancel hue wrap (after ColorEdit4 call), if any |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5933 |
if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5934 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5935 |
float new_H, new_S, new_V; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5936 |
ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5937 |
if (new_H <= 0 && H > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5938 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5939 |
if (new_V <= 0 && V != new_V) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5940 |
ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5941 |
else if (new_S <= 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5942 |
ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5943 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5944 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5945 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5946 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5947 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5948 |
if (flags & ImGuiColorEditFlags_InputRGB) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5949 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5950 |
R = col[0]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5951 |
G = col[1]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5952 |
B = col[2]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5953 |
ColorConvertRGBtoHSV(R, G, B, H, S, V); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5954 |
ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5955 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5956 |
else if (flags & ImGuiColorEditFlags_InputHSV) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5957 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5958 |
H = col[0]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5959 |
S = col[1]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5960 |
V = col[2]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5961 |
ColorConvertHSVtoRGB(H, S, V, R, G, B); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5962 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5963 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5964 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5965 |
const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5966 |
const ImU32 col_black = IM_COL32(0,0,0,style_alpha8); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5967 |
const ImU32 col_white = IM_COL32(255,255,255,style_alpha8); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5968 |
const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5969 |
const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5970 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5971 |
ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5972 |
ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5973 |
ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)); // Important: this is still including the main rendering/style alpha!! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5974 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5975 |
ImVec2 sv_cursor_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5976 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5977 |
if (flags & ImGuiColorEditFlags_PickerHueWheel) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5978 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5979 |
// Render Hue Wheel |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5980 |
const float aeps = 0.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5981 |
const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5982 |
for (int n = 0; n < 6; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5983 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5984 |
const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5985 |
const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5986 |
const int vert_start_idx = draw_list->VtxBuffer.Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5987 |
draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5988 |
draw_list->PathStroke(col_white, 0, wheel_thickness); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5989 |
const int vert_end_idx = draw_list->VtxBuffer.Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5990 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5991 |
// Paint colors over existing vertices |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5992 |
ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5993 |
ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5994 |
ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5995 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5996 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5997 |
// Render Cursor + preview on Hue Wheel |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5998 |
float cos_hue_angle = ImCos(H * 2.0f * IM_PI); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
5999 |
float sin_hue_angle = ImSin(H * 2.0f * IM_PI); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6000 |
ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6001 |
float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6002 |
int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6003 |
draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6004 |
draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6005 |
draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6006 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6007 |
// Render SV triangle (rotated according to hue) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6008 |
ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6009 |
ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6010 |
ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6011 |
ImVec2 uv_white = GetFontTexUvWhitePixel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6012 |
draw_list->PrimReserve(3, 3); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6013 |
draw_list->PrimVtx(tra, uv_white, hue_color32); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6014 |
draw_list->PrimVtx(trb, uv_white, col_black); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6015 |
draw_list->PrimVtx(trc, uv_white, col_white); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6016 |
draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6017 |
sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6018 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6019 |
else if (flags & ImGuiColorEditFlags_PickerHueBar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6020 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6021 |
// Render SV Square |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6022 |
draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6023 |
draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6024 |
RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6025 |
sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S) * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6026 |
sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6027 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6028 |
// Render Hue Bar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6029 |
for (int i = 0; i < 6; ++i) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6030 |
draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6031 |
float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6032 |
RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6033 |
RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6034 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6035 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6036 |
// Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6037 |
float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6038 |
int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6039 |
draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6040 |
draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6041 |
draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6042 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6043 |
// Render alpha bar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6044 |
if (alpha_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6045 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6046 |
float alpha = ImSaturate(col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6047 |
ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6048 |
RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6049 |
draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6050 |
float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6051 |
RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6052 |
RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6053 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6054 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6055 |
EndGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6056 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6057 |
if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6058 |
value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6059 |
if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6060 |
MarkItemEdited(g.LastItemData.ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6061 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6062 |
if (set_current_color_edit_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6063 |
g.ColorEditCurrentID = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6064 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6065 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6066 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6067 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6068 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6069 |
// A little color square. Return true when clicked. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6070 |
// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6071 |
// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6072 |
// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6073 |
bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6074 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6075 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6076 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6077 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6078 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6079 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6080 |
const ImGuiID id = window->GetID(desc_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6081 |
const float default_size = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6082 |
const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6083 |
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6084 |
ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6085 |
if (!ItemAdd(bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6086 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6087 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6088 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6089 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6090 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6091 |
if (flags & ImGuiColorEditFlags_NoAlpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6092 |
flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6093 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6094 |
ImVec4 col_rgb = col; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6095 |
if (flags & ImGuiColorEditFlags_InputHSV) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6096 |
ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6097 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6098 |
ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6099 |
float grid_step = ImMin(size.x, size.y) / 2.99f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6100 |
float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6101 |
ImRect bb_inner = bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6102 |
float off = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6103 |
if ((flags & ImGuiColorEditFlags_NoBorder) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6104 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6105 |
off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6106 |
bb_inner.Expand(off); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6107 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6108 |
if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6109 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6110 |
float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6111 |
RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6112 |
window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6113 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6114 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6115 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6116 |
// Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6117 |
ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6118 |
if (col_source.w < 1.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6119 |
RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6120 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6121 |
window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6122 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6123 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6124 |
if ((flags & ImGuiColorEditFlags_NoBorder) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6125 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6126 |
if (g.Style.FrameBorderSize > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6127 |
RenderFrameBorder(bb.Min, bb.Max, rounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6128 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6129 |
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6130 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6131 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6132 |
// Drag and Drop Source |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6133 |
// NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6134 |
if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6135 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6136 |
if (flags & ImGuiColorEditFlags_NoAlpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6137 |
SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6138 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6139 |
SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6140 |
ColorButton(desc_id, col, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6141 |
SameLine(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6142 |
TextEx("Color"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6143 |
EndDragDropSource(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6144 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6145 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6146 |
// Tooltip |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6147 |
if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6148 |
ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6149 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6150 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6151 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6152 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6153 |
// Initialize/override default color options |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6154 |
void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6155 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6156 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6157 |
if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6158 |
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6159 |
if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6160 |
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6161 |
if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6162 |
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6163 |
if ((flags & ImGuiColorEditFlags_InputMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6164 |
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6165 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6166 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6167 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6168 |
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6169 |
g.ColorEditOptions = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6170 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6171 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6172 |
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6173 |
void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6174 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6175 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6176 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6177 |
if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6178 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6179 |
const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6180 |
if (text_end > text) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6181 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6182 |
TextEx(text, text_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6183 |
Separator(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6184 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6185 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6186 |
ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6187 |
ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6188 |
int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6189 |
ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6190 |
SameLine(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6191 |
if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6192 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6193 |
if (flags & ImGuiColorEditFlags_NoAlpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6194 |
Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6195 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6196 |
Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6197 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6198 |
else if (flags & ImGuiColorEditFlags_InputHSV) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6199 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6200 |
if (flags & ImGuiColorEditFlags_NoAlpha) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6201 |
Text("H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6202 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6203 |
Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6204 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6205 |
EndTooltip(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6206 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6207 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6208 |
void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6209 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6210 |
bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6211 |
bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6212 |
if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6213 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6214 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6215 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6216 |
PushItemFlag(ImGuiItemFlags_NoMarkEdited, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6217 |
ImGuiColorEditFlags opts = g.ColorEditOptions; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6218 |
if (allow_opt_inputs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6219 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6220 |
if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6221 |
if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6222 |
if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6223 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6224 |
if (allow_opt_datatype) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6225 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6226 |
if (allow_opt_inputs) Separator(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6227 |
if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6228 |
if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6229 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6230 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6231 |
if (allow_opt_inputs || allow_opt_datatype) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6232 |
Separator(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6233 |
if (Button("Copy as..", ImVec2(-1, 0))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6234 |
OpenPopup("Copy"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6235 |
if (BeginPopup("Copy")) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6236 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6237 |
int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6238 |
char buf[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6239 |
ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6240 |
if (Selectable(buf)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6241 |
SetClipboardText(buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6242 |
ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6243 |
if (Selectable(buf)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6244 |
SetClipboardText(buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6245 |
ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6246 |
if (Selectable(buf)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6247 |
SetClipboardText(buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6248 |
if (!(flags & ImGuiColorEditFlags_NoAlpha)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6249 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6250 |
ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6251 |
if (Selectable(buf)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6252 |
SetClipboardText(buf); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6253 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6254 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6255 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6256 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6257 |
g.ColorEditOptions = opts; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6258 |
PopItemFlag(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6259 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6260 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6261 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6262 |
void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6263 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6264 |
bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6265 |
bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6266 |
if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6267 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6268 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6269 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6270 |
PushItemFlag(ImGuiItemFlags_NoMarkEdited, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6271 |
if (allow_opt_picker) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6272 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6273 |
ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6274 |
PushItemWidth(picker_size.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6275 |
for (int picker_type = 0; picker_type < 2; picker_type++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6276 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6277 |
// Draw small/thumbnail version of each picker type (over an invisible button for selection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6278 |
if (picker_type > 0) Separator(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6279 |
PushID(picker_type); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6280 |
ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6281 |
if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6282 |
if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6283 |
ImVec2 backup_pos = GetCursorScreenPos(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6284 |
if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6285 |
g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6286 |
SetCursorScreenPos(backup_pos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6287 |
ImVec4 previewing_ref_col; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6288 |
memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6289 |
ColorPicker4("##previewing_picker", &previewing_ref_col.x, picker_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6290 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6291 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6292 |
PopItemWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6293 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6294 |
if (allow_opt_alpha_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6295 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6296 |
if (allow_opt_picker) Separator(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6297 |
CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6298 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6299 |
PopItemFlag(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6300 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6301 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6302 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6303 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6304 |
// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6305 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6306 |
// - TreeNode() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6307 |
// - TreeNodeV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6308 |
// - TreeNodeEx() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6309 |
// - TreeNodeExV() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6310 |
// - TreeNodeBehavior() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6311 |
// - TreePush() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6312 |
// - TreePop() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6313 |
// - GetTreeNodeToLabelSpacing() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6314 |
// - SetNextItemOpen() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6315 |
// - CollapsingHeader() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6316 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6317 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6318 |
bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6319 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6320 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6321 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6322 |
bool is_open = TreeNodeExV(str_id, 0, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6323 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6324 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6325 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6326 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6327 |
bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6328 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6329 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6330 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6331 |
bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6332 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6333 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6334 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6335 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6336 |
bool ImGui::TreeNode(const char* label) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6337 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6338 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6339 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6340 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6341 |
ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6342 |
return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6343 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6344 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6345 |
bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6346 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6347 |
return TreeNodeExV(str_id, 0, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6348 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6349 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6350 |
bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6351 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6352 |
return TreeNodeExV(ptr_id, 0, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6353 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6354 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6355 |
bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6356 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6357 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6358 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6359 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6360 |
ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6361 |
return TreeNodeBehavior(id, flags, label, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6362 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6363 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6364 |
bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6365 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6366 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6367 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6368 |
bool is_open = TreeNodeExV(str_id, flags, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6369 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6370 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6371 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6372 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6373 |
bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6374 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6375 |
va_list args; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6376 |
va_start(args, fmt); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6377 |
bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6378 |
va_end(args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6379 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6380 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6381 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6382 |
bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6383 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6384 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6385 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6386 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6387 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6388 |
ImGuiID id = window->GetID(str_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6389 |
const char* label, *label_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6390 |
ImFormatStringToTempBufferV(&label, &label_end, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6391 |
return TreeNodeBehavior(id, flags, label, label_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6392 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6393 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6394 |
bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6395 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6396 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6397 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6398 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6399 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6400 |
ImGuiID id = window->GetID(ptr_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6401 |
const char* label, *label_end; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6402 |
ImFormatStringToTempBufferV(&label, &label_end, fmt, args); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6403 |
return TreeNodeBehavior(id, flags, label, label_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6404 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6405 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6406 |
bool ImGui::TreeNodeGetOpen(ImGuiID storage_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6407 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6408 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6409 |
ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6410 |
return storage->GetInt(storage_id, 0) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6411 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6412 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6413 |
void ImGui::TreeNodeSetOpen(ImGuiID storage_id, bool open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6414 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6415 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6416 |
ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6417 |
storage->SetInt(storage_id, open ? 1 : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6418 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6419 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6420 |
bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6421 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6422 |
if (flags & ImGuiTreeNodeFlags_Leaf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6423 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6424 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6425 |
// We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6426 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6427 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6428 |
ImGuiStorage* storage = window->DC.StateStorage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6429 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6430 |
bool is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6431 |
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasOpen) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6432 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6433 |
if (g.NextItemData.OpenCond & ImGuiCond_Always) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6434 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6435 |
is_open = g.NextItemData.OpenVal; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6436 |
TreeNodeSetOpen(storage_id, is_open); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6437 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6438 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6439 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6440 |
// We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6441 |
const int stored_value = storage->GetInt(storage_id, -1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6442 |
if (stored_value == -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6443 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6444 |
is_open = g.NextItemData.OpenVal; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6445 |
TreeNodeSetOpen(storage_id, is_open); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6446 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6447 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6448 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6449 |
is_open = stored_value != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6450 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6451 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6452 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6453 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6454 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6455 |
is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6456 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6457 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6458 |
// When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6459 |
// NB- If we are above max depth we still allow manually opened nodes to be logged. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6460 |
if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6461 |
is_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6462 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6463 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6464 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6465 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6466 |
// Store ImGuiTreeNodeStackData for just submitted node. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6467 |
// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6468 |
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6469 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6470 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6471 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6472 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6473 |
g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6474 |
ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6475 |
tree_node_data->ID = g.LastItemData.ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6476 |
tree_node_data->TreeFlags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6477 |
tree_node_data->ItemFlags = g.LastItemData.ItemFlags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6478 |
tree_node_data->NavRect = g.LastItemData.NavRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6479 |
window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6480 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6481 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6482 |
// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6483 |
bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6484 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6485 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6486 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6487 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6488 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6489 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6490 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6491 |
const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6492 |
const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6493 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6494 |
if (!label_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6495 |
label_end = FindRenderedTextEnd(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6496 |
const ImVec2 label_size = CalcTextSize(label, label_end, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6497 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6498 |
const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6499 |
const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6500 |
const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6501 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6502 |
// We vertically grow up to current line height up the typical widget height. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6503 |
const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6504 |
const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6505 |
ImRect frame_bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6506 |
frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6507 |
frame_bb.Min.y = window->DC.CursorPos.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6508 |
frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanTextWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6509 |
frame_bb.Max.y = window->DC.CursorPos.y + frame_height; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6510 |
if (display_frame) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6511 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6512 |
const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6513 |
frame_bb.Min.x -= outer_extend; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6514 |
frame_bb.Max.x += outer_extend; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6515 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6516 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6517 |
ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6518 |
ItemSize(ImVec2(text_width, frame_height), padding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6519 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6520 |
// For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6521 |
ImRect interact_bb = frame_bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6522 |
if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6523 |
interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6524 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6525 |
// Compute open and multi-select states before ItemAdd() as it clear NextItem data. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6526 |
ImGuiID storage_id = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6527 |
bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6528 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6529 |
bool is_visible; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6530 |
if (span_all_columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6531 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6532 |
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6533 |
const float backup_clip_rect_min_x = window->ClipRect.Min.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6534 |
const float backup_clip_rect_max_x = window->ClipRect.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6535 |
window->ClipRect.Min.x = window->ParentWorkRect.Min.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6536 |
window->ClipRect.Max.x = window->ParentWorkRect.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6537 |
is_visible = ItemAdd(interact_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6538 |
window->ClipRect.Min.x = backup_clip_rect_min_x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6539 |
window->ClipRect.Max.x = backup_clip_rect_max_x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6540 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6541 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6542 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6543 |
is_visible = ItemAdd(interact_bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6544 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6545 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6546 |
g.LastItemData.DisplayRect = frame_bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6547 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6548 |
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6549 |
// Store data for the current depth to allow returning to this node from any child item. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6550 |
// For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6551 |
// It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6552 |
bool store_tree_node_stack_data = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6553 |
if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6554 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6555 |
if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6556 |
if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6557 |
store_tree_node_stack_data = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6558 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6559 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6560 |
const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6561 |
if (!is_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6562 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6563 |
if (store_tree_node_stack_data && is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6564 |
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6565 |
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6566 |
TreePushOverrideID(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6567 |
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6568 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6569 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6570 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6571 |
if (span_all_columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6572 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6573 |
TablePushBackgroundChannel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6574 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6575 |
g.LastItemData.ClipRect = window->ClipRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6576 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6577 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6578 |
ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6579 |
if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6580 |
button_flags |= ImGuiButtonFlags_AllowOverlap; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6581 |
if (!is_leaf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6582 |
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6583 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6584 |
// We allow clicking on the arrow section with keyboard modifiers held, in order to easily |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6585 |
// allow browsing a tree while preserving selection with code implementing multi-selection patterns. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6586 |
// When clicking on the rest of the tree node we always disallow keyboard modifiers. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6587 |
const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6588 |
const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6589 |
const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6590 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6591 |
const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6592 |
if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes by default |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6593 |
flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6594 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6595 |
// Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6596 |
// Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6597 |
// - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6598 |
// - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6599 |
// - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6600 |
// - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6601 |
// - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6602 |
// It is rather standard that arrow click react on Down rather than Up. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6603 |
// We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6604 |
if (is_mouse_x_over_arrow) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6605 |
button_flags |= ImGuiButtonFlags_PressedOnClick; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6606 |
else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6607 |
button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6608 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6609 |
button_flags |= ImGuiButtonFlags_PressedOnClickRelease; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6610 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6611 |
bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6612 |
const bool was_selected = selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6613 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6614 |
// Multi-selection support (header) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6615 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6616 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6617 |
// Handle multi-select + alter button flags for it |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6618 |
MultiSelectItemHeader(id, &selected, &button_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6619 |
if (is_mouse_x_over_arrow) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6620 |
button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6621 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6622 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6623 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6624 |
if (window != g.HoveredWindow || !is_mouse_x_over_arrow) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6625 |
button_flags |= ImGuiButtonFlags_NoKeyModsAllowed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6626 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6627 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6628 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6629 |
bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6630 |
bool toggled = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6631 |
if (!is_leaf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6632 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6633 |
if (pressed && g.DragDropHoldJustPressedId != id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6634 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6635 |
if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (g.NavActivateId == id && !is_multi_select)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6636 |
toggled = true; // Single click |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6637 |
if (flags & ImGuiTreeNodeFlags_OpenOnArrow) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6638 |
toggled |= is_mouse_x_over_arrow && !g.NavHighlightItemUnderNav; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6639 |
if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6640 |
toggled = true; // Double click |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6641 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6642 |
else if (pressed && g.DragDropHoldJustPressedId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6643 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6644 |
IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6645 |
if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6646 |
toggled = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6647 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6648 |
pressed = false; // Cancel press so it doesn't trigger selection. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6649 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6650 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6651 |
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6652 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6653 |
toggled = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6654 |
NavClearPreferredPosForAxis(ImGuiAxis_X); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6655 |
NavMoveRequestCancel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6656 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6657 |
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6658 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6659 |
toggled = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6660 |
NavClearPreferredPosForAxis(ImGuiAxis_X); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6661 |
NavMoveRequestCancel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6662 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6663 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6664 |
if (toggled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6665 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6666 |
is_open = !is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6667 |
window->DC.StateStorage->SetInt(storage_id, is_open); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6668 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6669 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6670 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6671 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6672 |
// Multi-selection support (footer) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6673 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6674 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6675 |
bool pressed_copy = pressed && !toggled; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6676 |
MultiSelectItemFooter(id, &selected, &pressed_copy); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6677 |
if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6678 |
SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6679 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6680 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6681 |
if (selected != was_selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6682 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6683 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6684 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6685 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6686 |
const ImU32 text_col = GetColorU32(ImGuiCol_Text); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6687 |
ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6688 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6689 |
nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6690 |
if (display_frame) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6691 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6692 |
// Framed type |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6693 |
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6694 |
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6695 |
RenderNavCursor(frame_bb, id, nav_render_cursor_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6696 |
if (flags & ImGuiTreeNodeFlags_Bullet) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6697 |
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6698 |
else if (!is_leaf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6699 |
RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6700 |
else // Leaf without bullet, left-adjusted text |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6701 |
text_pos.x -= text_offset_x - padding.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6702 |
if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6703 |
frame_bb.Max.x -= g.FontSize + style.FramePadding.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6704 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6705 |
LogSetNextTextDecoration("###", "###"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6706 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6707 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6708 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6709 |
// Unframed typed for tree nodes |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6710 |
if (hovered || selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6711 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6712 |
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6713 |
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6714 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6715 |
RenderNavCursor(frame_bb, id, nav_render_cursor_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6716 |
if (flags & ImGuiTreeNodeFlags_Bullet) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6717 |
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6718 |
else if (!is_leaf) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6719 |
RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6720 |
if (g.LogEnabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6721 |
LogSetNextTextDecoration(">", NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6722 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6723 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6724 |
if (span_all_columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6725 |
TablePopBackgroundChannel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6726 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6727 |
// Label |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6728 |
if (display_frame) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6729 |
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6730 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6731 |
RenderText(text_pos, label, label_end, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6732 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6733 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6734 |
if (store_tree_node_stack_data && is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6735 |
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6736 |
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6737 |
TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6738 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6739 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6740 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6741 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6742 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6743 |
void ImGui::TreePush(const char* str_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6744 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6745 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6746 |
Indent(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6747 |
window->DC.TreeDepth++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6748 |
PushID(str_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6749 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6750 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6751 |
void ImGui::TreePush(const void* ptr_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6752 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6753 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6754 |
Indent(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6755 |
window->DC.TreeDepth++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6756 |
PushID(ptr_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6757 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6758 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6759 |
void ImGui::TreePushOverrideID(ImGuiID id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6760 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6761 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6762 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6763 |
Indent(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6764 |
window->DC.TreeDepth++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6765 |
PushOverrideID(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6766 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6767 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6768 |
void ImGui::TreePop() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6769 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6770 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6771 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6772 |
Unindent(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6773 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6774 |
window->DC.TreeDepth--; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6775 |
ImU32 tree_depth_mask = (1 << window->DC.TreeDepth); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6776 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6777 |
if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask) // Only set during request |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6778 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6779 |
ImGuiTreeNodeStackData* data = &g.TreeNodeStack.back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6780 |
IM_ASSERT(data->ID == window->IDStack.back()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6781 |
if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6782 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6783 |
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6784 |
if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6785 |
NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6786 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6787 |
g.TreeNodeStack.pop_back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6788 |
window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6789 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6790 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6791 |
IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6792 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6793 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6794 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6795 |
// Horizontal distance preceding label when using TreeNode() or Bullet() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6796 |
float ImGui::GetTreeNodeToLabelSpacing() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6797 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6798 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6799 |
return g.FontSize + (g.Style.FramePadding.x * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6800 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6801 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6802 |
// Set next TreeNode/CollapsingHeader open state. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6803 |
void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6804 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6805 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6806 |
if (g.CurrentWindow->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6807 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6808 |
g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasOpen; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6809 |
g.NextItemData.OpenVal = is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6810 |
g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6811 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6812 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6813 |
// Set next TreeNode/CollapsingHeader storage id. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6814 |
void ImGui::SetNextItemStorageID(ImGuiID storage_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6815 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6816 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6817 |
if (g.CurrentWindow->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6818 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6819 |
g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasStorageID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6820 |
g.NextItemData.StorageId = storage_id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6821 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6822 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6823 |
// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6824 |
// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6825 |
bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6826 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6827 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6828 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6829 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6830 |
ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6831 |
return TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6832 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6833 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6834 |
// p_visible == NULL : regular collapsing header |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6835 |
// p_visible != NULL && *p_visible == true : show a small close button on the corner of the header, clicking the button will set *p_visible = false |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6836 |
// p_visible != NULL && *p_visible == false : do not show the header at all |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6837 |
// Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags_DefaultOpen. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6838 |
bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6839 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6840 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6841 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6842 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6843 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6844 |
if (p_visible && !*p_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6845 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6846 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6847 |
ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6848 |
flags |= ImGuiTreeNodeFlags_CollapsingHeader; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6849 |
if (p_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6850 |
flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6851 |
bool is_open = TreeNodeBehavior(id, flags, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6852 |
if (p_visible != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6853 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6854 |
// Create a small overlapping close button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6855 |
// FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6856 |
// FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6857 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6858 |
ImGuiLastItemData last_item_backup = g.LastItemData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6859 |
float button_size = g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6860 |
float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6861 |
float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6862 |
ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6863 |
if (CloseButton(close_button_id, ImVec2(button_x, button_y))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6864 |
*p_visible = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6865 |
g.LastItemData = last_item_backup; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6866 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6867 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6868 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6869 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6870 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6871 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6872 |
// [SECTION] Widgets: Selectable |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6873 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6874 |
// - Selectable() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6875 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6876 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6877 |
// Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6878 |
// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6879 |
// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6880 |
// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6881 |
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6882 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6883 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6884 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6885 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6886 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6887 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6888 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6889 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6890 |
// Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6891 |
ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6892 |
ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6893 |
ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6894 |
ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6895 |
pos.y += window->DC.CurrLineTextBaseOffset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6896 |
ItemSize(size, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6897 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6898 |
// Fill horizontal space |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6899 |
// We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6900 |
const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6901 |
const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6902 |
const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6903 |
if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6904 |
size.x = ImMax(label_size.x, max_x - min_x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6905 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6906 |
// Text stays at the submission position, but bounding box may be extended on both sides |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6907 |
const ImVec2 text_min = pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6908 |
const ImVec2 text_max(min_x + size.x, pos.y + size.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6909 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6910 |
// Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6911 |
// FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6912 |
ImRect bb(min_x, pos.y, text_max.x, text_max.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6913 |
if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6914 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6915 |
const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6916 |
const float spacing_y = style.ItemSpacing.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6917 |
const float spacing_L = IM_TRUNC(spacing_x * 0.50f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6918 |
const float spacing_U = IM_TRUNC(spacing_y * 0.50f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6919 |
bb.Min.x -= spacing_L; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6920 |
bb.Min.y -= spacing_U; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6921 |
bb.Max.x += (spacing_x - spacing_L); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6922 |
bb.Max.y += (spacing_y - spacing_U); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6923 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6924 |
//if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6925 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6926 |
const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6927 |
const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6928 |
bool is_visible; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6929 |
if (span_all_columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6930 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6931 |
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6932 |
const float backup_clip_rect_min_x = window->ClipRect.Min.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6933 |
const float backup_clip_rect_max_x = window->ClipRect.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6934 |
window->ClipRect.Min.x = window->ParentWorkRect.Min.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6935 |
window->ClipRect.Max.x = window->ParentWorkRect.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6936 |
is_visible = ItemAdd(bb, id, NULL, extra_item_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6937 |
window->ClipRect.Min.x = backup_clip_rect_min_x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6938 |
window->ClipRect.Max.x = backup_clip_rect_max_x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6939 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6940 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6941 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6942 |
is_visible = ItemAdd(bb, id, NULL, extra_item_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6943 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6944 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6945 |
const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6946 |
if (!is_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6947 |
if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) // Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6948 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6949 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6950 |
const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6951 |
if (disabled_item && !disabled_global) // Only testing this as an optimization |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6952 |
BeginDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6953 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6954 |
// FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6955 |
// which would be advantageous since most selectable are not selected. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6956 |
if (span_all_columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6957 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6958 |
if (g.CurrentTable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6959 |
TablePushBackgroundChannel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6960 |
else if (window->DC.CurrentColumns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6961 |
PushColumnsBackground(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6962 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6963 |
g.LastItemData.ClipRect = window->ClipRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6964 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6965 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6966 |
// We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6967 |
ImGuiButtonFlags button_flags = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6968 |
if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6969 |
if (flags & ImGuiSelectableFlags_NoSetKeyOwner) { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6970 |
if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6971 |
if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6972 |
if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6973 |
if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6974 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6975 |
// Multi-selection support (header) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6976 |
const bool was_selected = selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6977 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6978 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6979 |
// Handle multi-select + alter button flags for it |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6980 |
MultiSelectItemHeader(id, &selected, &button_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6981 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6982 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6983 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6984 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6985 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6986 |
// Multi-selection support (footer) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6987 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6988 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6989 |
MultiSelectItemFooter(id, &selected, &pressed); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6990 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6991 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6992 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6993 |
// Auto-select when moved into |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6994 |
// - This will be more fully fleshed in the range-select branch |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6995 |
// - This is not exposed as it won't nicely work with some user side handling of shift/control |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6996 |
// - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6997 |
// - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6998 |
// - (2) usage will fail with clipped items |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
6999 |
// The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7000 |
if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7001 |
if (g.NavJustMovedToId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7002 |
selected = pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7003 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7004 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7005 |
// Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7006 |
if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7007 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7008 |
if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7009 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7010 |
SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7011 |
if (g.IO.ConfigNavCursorVisibleAuto) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7012 |
g.NavCursorVisible = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7013 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7014 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7015 |
if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7016 |
MarkItemEdited(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7017 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7018 |
if (selected != was_selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7019 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7020 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7021 |
// Render |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7022 |
if (is_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7023 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7024 |
const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7025 |
if (highlighted || selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7026 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7027 |
// FIXME-MULTISELECT: Styling: Color for 'selected' elements? ImGuiCol_HeaderSelected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7028 |
ImU32 col; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7029 |
if (selected && !highlighted) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7030 |
col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7031 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7032 |
col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7033 |
RenderFrame(bb.Min, bb.Max, col, false, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7034 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7035 |
if (g.NavId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7036 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7037 |
ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7038 |
if (is_multi_select) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7039 |
nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7040 |
RenderNavCursor(bb, id, nav_render_cursor_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7041 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7042 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7043 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7044 |
if (span_all_columns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7045 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7046 |
if (g.CurrentTable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7047 |
TablePopBackgroundChannel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7048 |
else if (window->DC.CurrentColumns) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7049 |
PopColumnsBackground(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7050 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7051 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7052 |
if (is_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7053 |
RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7054 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7055 |
// Automatically close popups |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7056 |
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7057 |
CloseCurrentPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7058 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7059 |
if (disabled_item && !disabled_global) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7060 |
EndDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7061 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7062 |
// Selectable() always returns a pressed state! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7063 |
// Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui::IsItemToggledSelection() to retrieve |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7064 |
// selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7065 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7066 |
return pressed; //-V1020 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7067 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7068 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7069 |
bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7070 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7071 |
if (Selectable(label, *p_selected, flags, size_arg)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7072 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7073 |
*p_selected = !*p_selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7074 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7075 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7076 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7077 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7078 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7079 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7080 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7081 |
// [SECTION] Widgets: Typing-Select support |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7082 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7083 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7084 |
// [Experimental] Currently not exposed in public API. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7085 |
// Consume character inputs and return search request, if any. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7086 |
// This would typically only be called on the focused window or location you want to grab inputs for, e.g. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7087 |
// if (ImGui::IsWindowFocused(...)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7088 |
// if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7089 |
// focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7090 |
// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7091 |
ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7092 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7093 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7094 |
ImGuiTypingSelectState* data = &g.TypingSelectState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7095 |
ImGuiTypingSelectRequest* out_request = &data->Request; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7096 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7097 |
// Clear buffer |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7098 |
const float TYPING_SELECT_RESET_TIMER = 1.80f; // FIXME: Potentially move to IO config. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7099 |
const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7100 |
if (data->SearchBuffer[0] != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7101 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7102 |
bool clear_buffer = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7103 |
clear_buffer |= (g.NavFocusScopeId != data->FocusScope); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7104 |
clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7105 |
clear_buffer |= g.NavAnyRequest; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7106 |
clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7107 |
clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7108 |
clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7109 |
//if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7110 |
if (clear_buffer) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7111 |
data->Clear(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7112 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7113 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7114 |
// Append to buffer |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7115 |
const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7116 |
int buffer_len = (int)strlen(data->SearchBuffer); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7117 |
bool select_request = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7118 |
for (ImWchar w : g.IO.InputQueueCharacters) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7119 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7120 |
const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7121 |
if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7122 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7123 |
char w_buf[5]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7124 |
ImTextCharToUtf8(w_buf, (unsigned int)w); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7125 |
if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7126 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7127 |
select_request = true; // Same character: don't need to append to buffer. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7128 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7129 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7130 |
if (data->SingleCharModeLock) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7131 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7132 |
data->Clear(); // Different character: clear |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7133 |
buffer_len = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7134 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7135 |
memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7136 |
buffer_len += w_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7137 |
select_request = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7138 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7139 |
g.IO.InputQueueCharacters.resize(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7140 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7141 |
// Handle backspace |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7142 |
if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7143 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7144 |
char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7145 |
*p = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7146 |
buffer_len = (int)(p - data->SearchBuffer); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7147 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7148 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7149 |
// Return request if any |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7150 |
if (buffer_len == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7151 |
return NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7152 |
if (select_request) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7153 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7154 |
data->FocusScope = g.NavFocusScopeId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7155 |
data->LastRequestFrame = g.FrameCount; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7156 |
data->LastRequestTime = (float)g.Time; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7157 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7158 |
out_request->Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7159 |
out_request->SearchBufferLen = buffer_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7160 |
out_request->SearchBuffer = data->SearchBuffer; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7161 |
out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7162 |
out_request->SingleCharMode = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7163 |
out_request->SingleCharSize = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7164 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7165 |
// Calculate if buffer contains the same character repeated. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7166 |
// - This can be used to implement a special search mode on first character. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7167 |
// - Performed on UTF-8 codepoint for correctness. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7168 |
// - SingleCharMode is always set for first input character, because it usually leads to a "next". |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7169 |
if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7170 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7171 |
const char* buf_begin = out_request->SearchBuffer; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7172 |
const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7173 |
const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7174 |
const char* p = buf_begin + c0_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7175 |
for (; p < buf_end; p += c0_len) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7176 |
if (memcmp(buf_begin, p, (size_t)c0_len) != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7177 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7178 |
const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7179 |
out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7180 |
out_request->SingleCharSize = (ImS8)c0_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7181 |
data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7182 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7183 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7184 |
return out_request; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7185 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7186 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7187 |
static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7188 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7189 |
int match_len = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7190 |
while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7191 |
match_len++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7192 |
return match_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7193 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7194 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7195 |
// Default handler for finding a result for typing-select. You may implement your own. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7196 |
// You might want to display a tooltip to visualize the current request SearchBuffer |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7197 |
// When SingleCharMode is set: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7198 |
// - it is better to NOT display a tooltip of other on-screen display indicator. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7199 |
// - the index of the currently focused item is required. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7200 |
// if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7201 |
int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7202 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7203 |
if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7204 |
return -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7205 |
int idx = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7206 |
if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7207 |
idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7208 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7209 |
idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7210 |
if (idx != -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7211 |
SetNavCursorVisibleAfterMove(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7212 |
return idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7213 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7214 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7215 |
// Special handling when a single character is repeated: perform search on a single letter and goes to next. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7216 |
int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7217 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7218 |
// FIXME: Assume selection user data is index. Would be extremely practical. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7219 |
//if (nav_item_idx == -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7220 |
// nav_item_idx = (int)g.NavLastValidSelectionUserData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7221 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7222 |
int first_match_idx = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7223 |
bool return_next_match = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7224 |
for (int idx = 0; idx < items_count; idx++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7225 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7226 |
const char* item_name = get_item_name_func(user_data, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7227 |
if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7228 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7229 |
if (return_next_match) // Return next matching item after current item. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7230 |
return idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7231 |
if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7232 |
return idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7233 |
if (first_match_idx == -1) // Record first match for wrapping. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7234 |
first_match_idx = idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7235 |
if (nav_item_idx == idx) // Record that we encountering nav_item so we can return next match. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7236 |
return_next_match = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7237 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7238 |
return first_match_idx; // First result |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7239 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7240 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7241 |
int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7242 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7243 |
int longest_match_idx = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7244 |
int longest_match_len = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7245 |
for (int idx = 0; idx < items_count; idx++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7246 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7247 |
const char* item_name = get_item_name_func(user_data, idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7248 |
const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7249 |
if (match_len <= longest_match_len) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7250 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7251 |
longest_match_idx = idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7252 |
longest_match_len = match_len; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7253 |
if (match_len == req->SearchBufferLen) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7254 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7255 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7256 |
return longest_match_idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7257 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7258 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7259 |
void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7260 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7261 |
#ifndef IMGUI_DISABLE_DEBUG_TOOLS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7262 |
Text("SearchBuffer = \"%s\"", data->SearchBuffer); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7263 |
Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7264 |
Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7265 |
#else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7266 |
IM_UNUSED(data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7267 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7268 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7269 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7270 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7271 |
// [SECTION] Widgets: Box-Select support |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7272 |
// This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7273 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7274 |
// Extra logic in MultiSelectItemFooter() and ImGuiListClipper::Step() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7275 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7276 |
// - BoxSelectPreStartDrag() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7277 |
// - BoxSelectActivateDrag() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7278 |
// - BoxSelectDeactivateDrag() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7279 |
// - BoxSelectScrollWithMouseDrag() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7280 |
// - BeginBoxSelect() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7281 |
// - EndBoxSelect() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7282 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7283 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7284 |
// Call on the initial click. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7285 |
static void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7286 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7287 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7288 |
ImGuiBoxSelectState* bs = &g.BoxSelectState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7289 |
bs->ID = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7290 |
bs->IsStarting = true; // Consider starting box-select. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7291 |
bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7292 |
bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7293 |
bs->KeyMods = g.IO.KeyMods; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7294 |
bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7295 |
bs->ScrollAccum = ImVec2(0.0f, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7296 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7297 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7298 |
static void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7299 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7300 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7301 |
IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Activate\n", bs->ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7302 |
bs->IsActive = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7303 |
bs->Window = window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7304 |
bs->IsStarting = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7305 |
ImGui::SetActiveID(bs->ID, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7306 |
ImGui::SetActiveIdUsingAllKeyboardKeys(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7307 |
if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7308 |
bs->RequestClear = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7309 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7310 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7311 |
static void BoxSelectDeactivateDrag(ImGuiBoxSelectState* bs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7312 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7313 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7314 |
bs->IsActive = bs->IsStarting = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7315 |
if (g.ActiveId == bs->ID) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7316 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7317 |
IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Deactivate\n", bs->ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7318 |
ImGui::ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7319 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7320 |
bs->ID = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7321 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7322 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7323 |
static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window, const ImRect& inner_r) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7324 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7325 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7326 |
IM_ASSERT(bs->Window == window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7327 |
for (int n = 0; n < 2; n++) // each axis |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7328 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7329 |
const float mouse_pos = g.IO.MousePos[n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7330 |
const float dist = (mouse_pos > inner_r.Max[n]) ? mouse_pos - inner_r.Max[n] : (mouse_pos < inner_r.Min[n]) ? mouse_pos - inner_r.Min[n] : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7331 |
const float scroll_curr = window->Scroll[n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7332 |
if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n])) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7333 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7334 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7335 |
const float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7336 |
const float scroll_step = g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7337 |
bs->ScrollAccum[n] += scroll_step; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7338 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7339 |
// Accumulate into a stored value so we can handle high-framerate |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7340 |
const float scroll_step_i = ImFloor(bs->ScrollAccum[n]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7341 |
if (scroll_step_i == 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7342 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7343 |
if (n == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7344 |
ImGui::SetScrollX(window, scroll_curr + scroll_step_i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7345 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7346 |
ImGui::SetScrollY(window, scroll_curr + scroll_step_i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7347 |
bs->ScrollAccum[n] -= scroll_step_i; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7348 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7349 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7350 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7351 |
bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7352 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7353 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7354 |
ImGuiBoxSelectState* bs = &g.BoxSelectState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7355 |
KeepAliveID(box_select_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7356 |
if (bs->ID != box_select_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7357 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7358 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7359 |
// IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7360 |
bs->UnclipMode = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7361 |
bs->RequestClear = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7362 |
if (bs->IsStarting && IsMouseDragPastThreshold(0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7363 |
BoxSelectActivateDrag(bs, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7364 |
else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7365 |
BoxSelectDeactivateDrag(bs); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7366 |
if (!bs->IsActive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7367 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7368 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7369 |
// Current frame absolute prev/current rectangles are used to toggle selection. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7370 |
// They are derived from positions relative to scrolling space. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7371 |
ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7372 |
ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7373 |
ImVec2 curr_end_pos_abs = g.IO.MousePos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7374 |
if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7375 |
curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7376 |
bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7377 |
bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7378 |
bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7379 |
bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7380 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7381 |
// Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7382 |
// Storing an extra rect used by widgets supporting box-select. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7383 |
if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7384 |
if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7385 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7386 |
bs->UnclipMode = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7387 |
bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect x coordinates could be intersection of Prev and Curr rect on X axis. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7388 |
bs->UnclipRect.Add(bs->BoxSelectRectCurr); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7389 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7390 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7391 |
//GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7392 |
//GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7393 |
//GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7394 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7395 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7396 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7397 |
void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7398 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7399 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7400 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7401 |
ImGuiBoxSelectState* bs = &g.BoxSelectState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7402 |
IM_ASSERT(bs->IsActive); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7403 |
bs->UnclipMode = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7404 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7405 |
// Render selection rectangle |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7406 |
bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7407 |
ImRect box_select_r = bs->BoxSelectRectCurr; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7408 |
box_select_r.ClipWith(scope_rect); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7409 |
window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7410 |
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT: Styling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7411 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7412 |
// Scroll |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7413 |
const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7414 |
if (enable_scroll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7415 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7416 |
ImRect scroll_r = scope_rect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7417 |
scroll_r.Expand(-g.FontSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7418 |
//GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7419 |
if (!scroll_r.Contains(g.IO.MousePos)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7420 |
BoxSelectScrollWithMouseDrag(bs, window, scroll_r); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7421 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7422 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7423 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7424 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7425 |
// [SECTION] Widgets: Multi-Select support |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7426 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7427 |
// - DebugLogMultiSelectRequests() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7428 |
// - CalcScopeRect() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7429 |
// - BeginMultiSelect() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7430 |
// - EndMultiSelect() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7431 |
// - SetNextItemSelectionUserData() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7432 |
// - MultiSelectItemHeader() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7433 |
// - MultiSelectItemFooter() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7434 |
// - DebugNodeMultiSelectState() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7435 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7436 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7437 |
static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7438 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7439 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7440 |
IM_UNUSED(function); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7441 |
for (const ImGuiSelectionRequest& req : io->Requests) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7442 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7443 |
if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7444 |
if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d (dir %d)\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected, req.RangeDirection); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7445 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7446 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7447 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7448 |
static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7449 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7450 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7451 |
if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7452 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7453 |
// Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7454 |
return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7455 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7456 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7457 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7458 |
// When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7459 |
ImRect scope_rect = window->InnerClipRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7460 |
if (g.CurrentTable != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7461 |
scope_rect = g.CurrentTable->HostClipRect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7462 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7463 |
// Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7464 |
scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7465 |
return scope_rect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7466 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7467 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7468 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7469 |
// Return ImGuiMultiSelectIO structure. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7470 |
// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7471 |
// Passing 'selection_size' and 'items_count' parameters is currently optional. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7472 |
// - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags_ClearOnEscape won't claim Escape key when selection_size 0, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7473 |
// allowing a first press to clear selection THEN the second press to leave child window and return to parent. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7474 |
// - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7475 |
// - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7476 |
// - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags_ClearOnEscape flag dynamically. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7477 |
ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size, int items_count) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7478 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7479 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7480 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7481 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7482 |
if (++g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7483 |
g.MultiSelectTempData.resize(g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7484 |
ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7485 |
IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7486 |
g.CurrentMultiSelect = ms; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7487 |
if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7488 |
flags |= ImGuiMultiSelectFlags_ScopeWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7489 |
if (flags & ImGuiMultiSelectFlags_SingleSelect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7490 |
flags &= ~(ImGuiMultiSelectFlags_BoxSelect2d | ImGuiMultiSelectFlags_BoxSelect1d); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7491 |
if (flags & ImGuiMultiSelectFlags_BoxSelect2d) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7492 |
flags &= ~ImGuiMultiSelectFlags_BoxSelect1d; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7493 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7494 |
// FIXME: BeginFocusScope() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7495 |
const ImGuiID id = window->IDStack.back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7496 |
ms->Clear(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7497 |
ms->FocusScopeId = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7498 |
ms->Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7499 |
ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7500 |
ms->BackupCursorMaxPos = window->DC.CursorMaxPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7501 |
ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7502 |
PushFocusScope(ms->FocusScopeId); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7503 |
if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7504 |
window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7505 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7506 |
// Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7507 |
ms->KeyMods = g.NavJustMovedToId ? (g.NavJustMovedToIsTabbing ? 0 : g.NavJustMovedToKeyMods) : g.IO.KeyMods; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7508 |
if (flags & ImGuiMultiSelectFlags_NoRangeSelect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7509 |
ms->KeyMods &= ~ImGuiMod_Shift; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7510 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7511 |
// Bind storage |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7512 |
ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7513 |
storage->ID = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7514 |
storage->LastFrameActive = g.FrameCount; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7515 |
storage->LastSelectionSize = selection_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7516 |
storage->Window = window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7517 |
ms->Storage = storage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7518 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7519 |
// Output to user |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7520 |
ms->IO.Requests.resize(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7521 |
ms->IO.RangeSrcItem = storage->RangeSrcItem; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7522 |
ms->IO.NavIdItem = storage->NavIdItem; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7523 |
ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7524 |
ms->IO.ItemsCount = items_count; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7525 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7526 |
// Clear when using Navigation to move within the scope |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7527 |
// (we compare FocusScopeId so it possible to use multiple selections inside a same window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7528 |
bool request_clear = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7529 |
bool request_select_all = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7530 |
if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7531 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7532 |
if (ms->KeyMods & ImGuiMod_Shift) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7533 |
ms->IsKeyboardSetRange = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7534 |
if (ms->IsKeyboardSetRange) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7535 |
IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7536 |
if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7537 |
request_clear = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7538 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7539 |
else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7540 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7541 |
// Also clear on leaving scope (may be optional?) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7542 |
if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7543 |
request_clear = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7544 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7545 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7546 |
// Box-select handling: update active state. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7547 |
ImGuiBoxSelectState* bs = &g.BoxSelectState; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7548 |
if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7549 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7550 |
ms->BoxSelectId = GetID("##BoxSelect"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7551 |
if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7552 |
request_clear |= bs->RequestClear; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7553 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7554 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7555 |
if (ms->IsFocused) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7556 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7557 |
// Shortcut: Clear selection (Escape) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7558 |
// - Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7559 |
// - Box select also handle Escape and needs to pass an id to bypass ActiveIdUsingAllKeyboardKeys lock. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7560 |
if (flags & ImGuiMultiSelectFlags_ClearOnEscape) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7561 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7562 |
if (selection_size != 0 || bs->IsActive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7563 |
if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, bs->IsActive ? bs->ID : 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7564 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7565 |
request_clear = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7566 |
if (bs->IsActive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7567 |
BoxSelectDeactivateDrag(bs); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7568 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7569 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7570 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7571 |
// Shortcut: Select all (CTRL+A) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7572 |
if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7573 |
if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7574 |
request_select_all = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7575 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7576 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7577 |
if (request_clear || request_select_all) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7578 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7579 |
MultiSelectAddSetAll(ms, request_select_all); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7580 |
if (!request_select_all) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7581 |
storage->LastSelectionSize = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7582 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7583 |
ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7584 |
ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7585 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7586 |
if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7587 |
DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7588 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7589 |
return &ms->IO; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7590 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7591 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7592 |
// Return updated ImGuiMultiSelectIO structure. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7593 |
// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7594 |
ImGuiMultiSelectIO* ImGui::EndMultiSelect() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7595 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7596 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7597 |
ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7598 |
ImGuiMultiSelectState* storage = ms->Storage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7599 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7600 |
IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7601 |
IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7602 |
IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7603 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7604 |
ImRect scope_rect = CalcScopeRect(ms, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7605 |
if (ms->IsFocused) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7606 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7607 |
// We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7608 |
if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7609 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7610 |
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7611 |
storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7612 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7613 |
if (ms->NavIdPassedBy == false && storage->NavIdItem != ImGuiSelectionUserData_Invalid) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7614 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7615 |
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7616 |
storage->NavIdItem = ImGuiSelectionUserData_Invalid; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7617 |
storage->NavIdSelected = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7618 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7619 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7620 |
if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7621 |
EndBoxSelect(scope_rect, ms->Flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7622 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7623 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7624 |
if (ms->IsEndIO == false) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7625 |
ms->IO.Requests.resize(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7626 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7627 |
// Clear selection when clicking void? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7628 |
// We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7629 |
// The InnerRect test is necessary for non-child/decorated windows. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7630 |
bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7631 |
if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7632 |
scope_hovered &= scope_rect.Contains(g.IO.MousePos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7633 |
if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7634 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7635 |
if (ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7636 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7637 |
if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7638 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7639 |
BoxSelectPreStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7640 |
FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7641 |
SetHoveredID(ms->BoxSelectId); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7642 |
if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7643 |
SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from void to box-select. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7644 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7645 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7646 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7647 |
if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7648 |
if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7649 |
MultiSelectAddSetAll(ms, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7650 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7651 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7652 |
// Courtesy nav wrapping helper flag |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7653 |
if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7654 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7655 |
IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7656 |
ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7657 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7658 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7659 |
// Unwind |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7660 |
window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7661 |
PopFocusScope(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7662 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7663 |
if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7664 |
DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7665 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7666 |
ms->FocusScopeId = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7667 |
ms->Flags = ImGuiMultiSelectFlags_None; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7668 |
g.CurrentMultiSelect = (--g.MultiSelectTempDataStacked > 0) ? &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7669 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7670 |
return &ms->IO; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7671 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7672 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7673 |
void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7674 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7675 |
// Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7676 |
// This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7677 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7678 |
g.NextItemData.SelectionUserData = selection_user_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7679 |
g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7680 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7681 |
if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7682 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7683 |
// Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7684 |
g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7685 |
if (ms->IO.RangeSrcItem == selection_user_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7686 |
ms->RangeSrcPassedBy = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7687 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7688 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7689 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7690 |
g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7691 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7692 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7693 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7694 |
// In charge of: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7695 |
// - Applying SetAll for submitted items. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7696 |
// - Applying SetRange for submitted items and record end points. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7697 |
// - Altering button behavior flags to facilitate use with drag and drop. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7698 |
void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7699 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7700 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7701 |
ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7702 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7703 |
bool selected = *p_selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7704 |
if (ms->IsFocused) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7705 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7706 |
ImGuiMultiSelectState* storage = ms->Storage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7707 |
ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7708 |
IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7709 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7710 |
// Apply SetAll (Clear/SelectAll) requests requested by BeginMultiSelect(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7711 |
// This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7712 |
// If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7713 |
if (ms->LoopRequestSetAll != -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7714 |
selected = (ms->LoopRequestSetAll == 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7715 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7716 |
// When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7717 |
// For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7718 |
if (ms->IsKeyboardSetRange) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7719 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7720 |
IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7721 |
const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7722 |
if (is_range_dst) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7723 |
ms->RangeDstPassedBy = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7724 |
if (is_range_dst && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7725 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7726 |
storage->RangeSrcItem = item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7727 |
storage->RangeSelected = selected ? 1 : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7728 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7729 |
const bool is_range_src = storage->RangeSrcItem == item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7730 |
if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7731 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7732 |
// Apply range-select value to visible items |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7733 |
IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7734 |
selected = (storage->RangeSelected != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7735 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7736 |
else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0 && (ms->Flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7737 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7738 |
// Clear other items |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7739 |
selected = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7740 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7741 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7742 |
*p_selected = selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7743 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7744 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7745 |
// Alter button behavior flags |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7746 |
// To handle drag and drop of multiple items we need to avoid clearing selection on click. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7747 |
// Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7748 |
if (p_button_flags != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7749 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7750 |
ImGuiButtonFlags button_flags = *p_button_flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7751 |
button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7752 |
if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7753 |
button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7754 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7755 |
button_flags |= ImGuiButtonFlags_PressedOnClickRelease; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7756 |
*p_button_flags = button_flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7757 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7758 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7759 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7760 |
// In charge of: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7761 |
// - Auto-select on navigation. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7762 |
// - Box-select toggle handling. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7763 |
// - Right-click handling. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7764 |
// - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7765 |
// - Record current selection state for RangeSrc |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7766 |
// This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7767 |
void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7768 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7769 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7770 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7771 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7772 |
bool selected = *p_selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7773 |
bool pressed = *p_pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7774 |
ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7775 |
ImGuiMultiSelectState* storage = ms->Storage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7776 |
if (pressed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7777 |
ms->IsFocused = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7778 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7779 |
bool hovered = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7780 |
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7781 |
hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7782 |
if (!ms->IsFocused && !hovered) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7783 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7784 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7785 |
ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7786 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7787 |
ImGuiMultiSelectFlags flags = ms->Flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7788 |
const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7789 |
bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7790 |
bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7791 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7792 |
bool apply_to_range_src = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7793 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7794 |
if (g.NavId == id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7795 |
apply_to_range_src = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7796 |
if (ms->IsEndIO == false) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7797 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7798 |
ms->IO.Requests.resize(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7799 |
ms->IsEndIO = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7800 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7801 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7802 |
// Auto-select as you navigate a list |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7803 |
if (g.NavJustMovedToId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7804 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7805 |
if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7806 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7807 |
if (is_ctrl && is_shift) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7808 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7809 |
else if (!is_ctrl) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7810 |
selected = pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7811 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7812 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7813 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7814 |
// With NoAutoSelect, using Shift+keyboard performs a write/copy |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7815 |
if (is_shift) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7816 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7817 |
else if (!is_ctrl) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7818 |
apply_to_range_src = true; // Since if (pressed) {} main block is not running we update this |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7819 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7820 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7821 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7822 |
if (apply_to_range_src) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7823 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7824 |
storage->RangeSrcItem = item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7825 |
storage->RangeSelected = selected; // Will be updated at the end of this function anyway. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7826 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7827 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7828 |
// Box-select toggle handling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7829 |
if (ms->BoxSelectId != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7830 |
if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7831 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7832 |
const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7833 |
const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7834 |
if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7835 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7836 |
if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7837 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7838 |
pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7839 |
bs->IsStartedSetNavIdOnce = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7840 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7841 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7842 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7843 |
selected = !selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7844 |
MultiSelectAddSetRange(ms, selected, +1, item_data, item_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7845 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7846 |
storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7847 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7848 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7849 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7850 |
// Right-click handling. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7851 |
// FIXME-MULTISELECT: Currently filtered out by ImGuiMultiSelectFlags_NoAutoSelect but maybe should be moved to Selectable(). See https://github.com/ocornut/imgui/pull/5816 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7852 |
if (hovered && IsMouseClicked(1) && (flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7853 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7854 |
if (g.ActiveId != 0 && g.ActiveId != id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7855 |
ClearActiveID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7856 |
SetFocusID(id, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7857 |
if (!pressed && !selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7858 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7859 |
pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7860 |
is_ctrl = is_shift = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7861 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7862 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7863 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7864 |
// Unlike Space, Enter doesn't alter selection (but can still return a press) unless current item is not selected. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7865 |
// The later, "unless current item is not select", may become optional? It seems like a better default if Enter doesn't necessarily open something |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7866 |
// (unlike e.g. Windows explorer). For use case where Enter always open something, we might decide to make this optional? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7867 |
const bool enter_pressed = pressed && (g.NavActivateId == id) && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7868 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7869 |
// Alter selection |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7870 |
if (pressed && (!enter_pressed || !selected)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7871 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7872 |
// Box-select |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7873 |
ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7874 |
if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7875 |
if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7876 |
BoxSelectPreStartDrag(ms->BoxSelectId, item_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7877 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7878 |
//---------------------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7879 |
// ACTION | Begin | Pressed/Activated | End |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7880 |
//---------------------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7881 |
// Keys Navigated: | Clear | Src=item, Sel=1 SetRange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7882 |
// Keys Navigated: Ctrl | n/a | n/a |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7883 |
// Keys Navigated: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7884 |
// Keys Navigated: Ctrl+Shift | n/a | Dst=item, Sel=Src => Clear + SetRange Src-Dst |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7885 |
// Keys Activated: | n/a | Src=item, Sel=1 => Clear + SetRange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7886 |
// Keys Activated: Ctrl | n/a | Src=item, Sel=!Sel => SetSange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7887 |
// Keys Activated: Shift | n/a | Dst=item, Sel=1 => Clear + SetSange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7888 |
//---------------------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7889 |
// Mouse Pressed: | n/a | Src=item, Sel=1, => Clear + SetRange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7890 |
// Mouse Pressed: Ctrl | n/a | Src=item, Sel=!Sel => SetRange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7891 |
// Mouse Pressed: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7892 |
// Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7893 |
//---------------------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7894 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7895 |
if ((flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7896 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7897 |
bool request_clear = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7898 |
if (is_singleselect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7899 |
request_clear = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7900 |
else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7901 |
request_clear = (flags & ImGuiMultiSelectFlags_NoAutoClearOnReselect) ? !selected : true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7902 |
else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7903 |
request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7904 |
if (request_clear) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7905 |
MultiSelectAddSetAll(ms, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7906 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7907 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7908 |
int range_direction; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7909 |
bool range_selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7910 |
if (is_shift && !is_singleselect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7911 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7912 |
//IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7913 |
if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7914 |
storage->RangeSrcItem = item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7915 |
if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7916 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7917 |
// Shift+Arrow always select |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7918 |
// Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7919 |
range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7920 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7921 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7922 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7923 |
// Shift+Arrow copy source selection state |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7924 |
// Shift+Click always copy from target selection state |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7925 |
if (ms->IsKeyboardSetRange) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7926 |
range_selected = (storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7927 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7928 |
range_selected = !selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7929 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7930 |
range_direction = ms->RangeSrcPassedBy ? +1 : -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7931 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7932 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7933 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7934 |
// Ctrl inverts selection, otherwise always select |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7935 |
if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7936 |
selected = is_ctrl ? !selected : true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7937 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7938 |
selected = !selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7939 |
storage->RangeSrcItem = item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7940 |
range_selected = selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7941 |
range_direction = +1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7942 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7943 |
MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7944 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7945 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7946 |
// Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7947 |
if (storage->RangeSrcItem == item_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7948 |
storage->RangeSelected = selected ? 1 : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7949 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7950 |
// Update/store the selection state of focused item |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7951 |
if (g.NavId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7952 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7953 |
storage->NavIdItem = item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7954 |
storage->NavIdSelected = selected ? 1 : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7955 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7956 |
if (storage->NavIdItem == item_data) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7957 |
ms->NavIdPassedBy = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7958 |
ms->LastSubmittedItem = item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7959 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7960 |
*p_selected = selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7961 |
*p_pressed = pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7962 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7963 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7964 |
void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7965 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7966 |
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7967 |
ms->IO.Requests.resize(0); // Can always clear previous requests |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7968 |
ms->IO.Requests.push_back(req); // Add new request |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7969 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7970 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7971 |
void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7972 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7973 |
// Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7974 |
if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7975 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7976 |
ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7977 |
if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7978 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7979 |
prev->RangeLastItem = last_item; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7980 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7981 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7982 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7983 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7984 |
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7985 |
ms->IO.Requests.push_back(req); // Add new request |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7986 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7987 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7988 |
void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7989 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7990 |
#ifndef IMGUI_DISABLE_DEBUG_TOOLS |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7991 |
const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7992 |
if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7993 |
bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X in '%s'%s", storage->ID, storage->Window ? storage->Window->Name : "N/A", is_active ? "" : " *Inactive*"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7994 |
if (!is_active) { PopStyleColor(); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7995 |
if (!open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7996 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7997 |
Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7998 |
Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
7999 |
Text("LastSelectionSize = %d", storage->LastSelectionSize); // Provided by user |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8000 |
TreePop(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8001 |
#else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8002 |
IM_UNUSED(storage); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8003 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8004 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8005 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8006 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8007 |
// [SECTION] Widgets: Multi-Select helpers |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8008 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8009 |
// - ImGuiSelectionBasicStorage |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8010 |
// - ImGuiSelectionExternalStorage |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8011 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8012 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8013 |
ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8014 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8015 |
Size = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8016 |
PreserveOrder = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8017 |
UserData = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8018 |
AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8019 |
_SelectionOrder = 1; // Always >0 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8020 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8021 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8022 |
void ImGuiSelectionBasicStorage::Clear() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8023 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8024 |
Size = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8025 |
_SelectionOrder = 1; // Always >0 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8026 |
_Storage.Data.resize(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8027 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8028 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8029 |
void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8030 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8031 |
ImSwap(Size, r.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8032 |
ImSwap(_SelectionOrder, r._SelectionOrder); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8033 |
_Storage.Data.swap(r._Storage.Data); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8034 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8035 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8036 |
bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8037 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8038 |
return _Storage.GetInt(id, 0) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8039 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8040 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8041 |
static int IMGUI_CDECL PairComparerByValueInt(const void* lhs, const void* rhs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8042 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8043 |
int lhs_v = ((const ImGuiStoragePair*)lhs)->val_i; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8044 |
int rhs_v = ((const ImGuiStoragePair*)rhs)->val_i; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8045 |
return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8046 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8047 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8048 |
// GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8049 |
// (e.g. store unselected vs compact down, compact down on demand, use raw ImVector<ImGuiID> instead of ImGuiStorage...) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8050 |
bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8051 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8052 |
ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8053 |
ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8054 |
if (PreserveOrder && it == NULL && it_end != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8055 |
ImQsort(_Storage.Data.Data, (size_t)_Storage.Data.Size, sizeof(ImGuiStoragePair), PairComparerByValueInt); // ~ImGuiStorage::BuildSortByValueInt() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8056 |
if (it == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8057 |
it = _Storage.Data.Data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8058 |
IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8059 |
if (it != it_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8060 |
while (it->val_i == 0 && it < it_end) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8061 |
it++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8062 |
const bool has_more = (it != it_end); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8063 |
*opaque_it = has_more ? (void**)(it + 1) : (void**)(it); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8064 |
*out_id = has_more ? it->key : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8065 |
if (PreserveOrder && !has_more) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8066 |
_Storage.BuildSortByKey(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8067 |
return has_more; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8068 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8069 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8070 |
void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8071 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8072 |
int* p_int = _Storage.GetIntRef(id, 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8073 |
if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8074 |
else if (!selected && *p_int != 0) { *p_int = 0; Size--; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8075 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8076 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8077 |
// Optimized for batch edits (with same value of 'selected') |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8078 |
static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends, int selection_order) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8079 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8080 |
ImGuiStorage* storage = &selection->_Storage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8081 |
ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8082 |
const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8083 |
if (selected == (is_contained && it->val_i != 0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8084 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8085 |
if (selected && !is_contained) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8086 |
storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8087 |
else if (is_contained) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8088 |
it->val_i = selected ? selection_order : 0; // Modify in-place. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8089 |
selection->Size += selected ? +1 : -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8090 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8091 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8092 |
static void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection, bool selected, int size_before_amends) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8093 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8094 |
ImGuiStorage* storage = &selection->_Storage; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8095 |
if (selected && selection->Size != size_before_amends) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8096 |
storage->BuildSortByKey(); // When done selecting: sort everything |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8097 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8098 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8099 |
// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8100 |
// - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8101 |
// - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8102 |
// - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8103 |
// - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8104 |
// a lookup in order to have some way to iterate/interpolate between two items. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8105 |
// - A full-featured application is likely to allow search/filtering which is likely to lead to using indices |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8106 |
// and constructing a view index <> object id/ptr data structure anyway. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8107 |
// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8108 |
// Notice that with the simplest adapter (using indices everywhere), all functions return their parameters. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8109 |
// The most simple implementation (using indices everywhere) would look like: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8110 |
// for (ImGuiSelectionRequest& req : ms_io->Requests) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8111 |
// { |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8112 |
// if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { SetItemSelected(n, true); } } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8113 |
// if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { SetItemSelected(n, ms_io->Selected); } } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8114 |
// } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8115 |
void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8116 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8117 |
// For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8118 |
// It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8119 |
// Other scheme may handle SetAll differently. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8120 |
IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8121 |
IM_ASSERT(AdapterIndexToStorageId != NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8122 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8123 |
// This is optimized/specialized to cope with very large selections (e.g. 100k+ items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8124 |
// - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8125 |
// - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8126 |
// - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector<ImGuiID> to reduce bandwidth, but this is a reasonable trade off to reuse code. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8127 |
// - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8128 |
// left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8129 |
// FIXME-OPT: For each block of consecutive SetRange request: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8130 |
// - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8131 |
// - rewrite sorted storage a single time. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8132 |
for (ImGuiSelectionRequest& req : ms_io->Requests) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8133 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8134 |
if (req.Type == ImGuiSelectionRequestType_SetAll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8135 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8136 |
Clear(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8137 |
if (req.Selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8138 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8139 |
_Storage.Data.reserve(ms_io->ItemsCount); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8140 |
const int size_before_amends = _Storage.Data.Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8141 |
for (int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8142 |
ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8143 |
ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8144 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8145 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8146 |
else if (req.Type == ImGuiSelectionRequestType_SetRange) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8147 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8148 |
const int selection_changes = (int)req.RangeLastItem - (int)req.RangeFirstItem + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8149 |
//ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_SELECTION("Req %d/%d: set %d to %d\n", ms_io->Requests.index_from_ptr(&req), ms_io->Requests.Size, selection_changes, req.Selected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8150 |
if (selection_changes == 1 || (selection_changes < Size / 100)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8151 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8152 |
// Multiple sorted insertion + copy likely to be faster. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8153 |
// Technically we could do a single copy with a little more work (sort sequential SetRange requests) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8154 |
for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8155 |
SetItemSelected(GetStorageIdFromIndex(idx), req.Selected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8156 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8157 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8158 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8159 |
// Append insertion + single sort likely be faster. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8160 |
// Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8161 |
const int size_before_amends = _Storage.Data.Size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8162 |
int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8163 |
for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8164 |
ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8165 |
if (req.Selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8166 |
_SelectionOrder += selection_changes; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8167 |
ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8168 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8169 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8170 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8171 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8172 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8173 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8174 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8175 |
ImGuiSelectionExternalStorage::ImGuiSelectionExternalStorage() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8176 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8177 |
UserData = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8178 |
AdapterSetItemSelected = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8179 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8180 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8181 |
// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8182 |
// We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8183 |
// This makes no assumption about underlying storage. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8184 |
void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8185 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8186 |
IM_ASSERT(AdapterSetItemSelected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8187 |
for (ImGuiSelectionRequest& req : ms_io->Requests) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8188 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8189 |
if (req.Type == ImGuiSelectionRequestType_SetAll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8190 |
for (int idx = 0; idx < ms_io->ItemsCount; idx++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8191 |
AdapterSetItemSelected(this, idx, req.Selected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8192 |
if (req.Type == ImGuiSelectionRequestType_SetRange) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8193 |
for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8194 |
AdapterSetItemSelected(this, idx, req.Selected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8195 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8196 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8197 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8198 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8199 |
// [SECTION] Widgets: ListBox |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8200 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8201 |
// - BeginListBox() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8202 |
// - EndListBox() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8203 |
// - ListBox() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8204 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8205 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8206 |
// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8207 |
// This handle some subtleties with capturing info from the label, but for 99% uses it could essentially be rewritten as: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8208 |
// if (ImGui::BeginChild("...", ImVec2(ImGui::CalcItemWidth(), ImGui::GetTextLineHeight() * 7.5f), ImGuiChildFlags_FrameStyle)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8209 |
// { .... } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8210 |
// ImGui::EndChild(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8211 |
// ImGui::SameLine(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8212 |
// ImGui::AlignTextToFramePadding(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8213 |
// ImGui::Text("Label"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8214 |
// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8215 |
// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8216 |
bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8217 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8218 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8219 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8220 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8221 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8222 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8223 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8224 |
const ImGuiID id = GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8225 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8226 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8227 |
// Size default to hold ~7.25 items. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8228 |
// Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8229 |
ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8230 |
ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8231 |
ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8232 |
ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8233 |
g.NextItemData.ClearFlags(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8234 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8235 |
if (!IsRectVisible(bb.Min, bb.Max)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8236 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8237 |
ItemSize(bb.GetSize(), style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8238 |
ItemAdd(bb, 0, &frame_bb); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8239 |
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8240 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8241 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8242 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8243 |
// FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8244 |
BeginGroup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8245 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8246 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8247 |
ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8248 |
RenderText(label_pos, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8249 |
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8250 |
AlignTextToFramePadding(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8251 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8252 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8253 |
BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8254 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8255 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8256 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8257 |
void ImGui::EndListBox() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8258 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8259 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8260 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8261 |
IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8262 |
IM_UNUSED(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8263 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8264 |
EndChild(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8265 |
EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8266 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8267 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8268 |
bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8269 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8270 |
const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8271 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8272 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8273 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8274 |
// This is merely a helper around BeginListBox(), EndListBox(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8275 |
// Considering using those directly to submit custom data or store selection differently. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8276 |
bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8277 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8278 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8279 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8280 |
// Calculate size from "height_in_items" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8281 |
if (height_in_items < 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8282 |
height_in_items = ImMin(items_count, 7); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8283 |
float height_in_items_f = height_in_items + 0.25f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8284 |
ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8285 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8286 |
if (!BeginListBox(label, size)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8287 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8288 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8289 |
// Assume all items have even height (= 1 line of text). If you need items of different height, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8290 |
// you can create a custom version of ListBox() in your code without using the clipper. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8291 |
bool value_changed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8292 |
ImGuiListClipper clipper; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8293 |
clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8294 |
clipper.IncludeItemByIndex(*current_item); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8295 |
while (clipper.Step()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8296 |
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8297 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8298 |
const char* item_text = getter(user_data, i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8299 |
if (item_text == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8300 |
item_text = "*Unknown item*"; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8301 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8302 |
PushID(i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8303 |
const bool item_selected = (i == *current_item); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8304 |
if (Selectable(item_text, item_selected)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8305 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8306 |
*current_item = i; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8307 |
value_changed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8308 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8309 |
if (item_selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8310 |
SetItemDefaultFocus(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8311 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8312 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8313 |
EndListBox(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8314 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8315 |
if (value_changed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8316 |
MarkItemEdited(g.LastItemData.ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8317 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8318 |
return value_changed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8319 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8320 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8321 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8322 |
// [SECTION] Widgets: PlotLines, PlotHistogram |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8323 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8324 |
// - PlotEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8325 |
// - PlotLines() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8326 |
// - PlotHistogram() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8327 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8328 |
// Plot/Graph widgets are not very good. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8329 |
// Consider writing your own, or using a third-party one, see: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8330 |
// - ImPlot https://github.com/epezent/implot |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8331 |
// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8332 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8333 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8334 |
int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8335 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8336 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8337 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8338 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8339 |
return -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8340 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8341 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8342 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8343 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8344 |
const ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8345 |
const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8346 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8347 |
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8348 |
const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8349 |
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8350 |
ItemSize(total_bb, style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8351 |
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_NoNav)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8352 |
return -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8353 |
bool hovered; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8354 |
ButtonBehavior(frame_bb, id, &hovered, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8355 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8356 |
// Determine scale from values if not specified |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8357 |
if (scale_min == FLT_MAX || scale_max == FLT_MAX) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8358 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8359 |
float v_min = FLT_MAX; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8360 |
float v_max = -FLT_MAX; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8361 |
for (int i = 0; i < values_count; i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8362 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8363 |
const float v = values_getter(data, i); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8364 |
if (v != v) // Ignore NaN values |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8365 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8366 |
v_min = ImMin(v_min, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8367 |
v_max = ImMax(v_max, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8368 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8369 |
if (scale_min == FLT_MAX) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8370 |
scale_min = v_min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8371 |
if (scale_max == FLT_MAX) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8372 |
scale_max = v_max; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8373 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8374 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8375 |
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8376 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8377 |
const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8378 |
int idx_hovered = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8379 |
if (values_count >= values_count_min) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8380 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8381 |
int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8382 |
int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8383 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8384 |
// Tooltip on hover |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8385 |
if (hovered && inner_bb.Contains(g.IO.MousePos)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8386 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8387 |
const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8388 |
const int v_idx = (int)(t * item_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8389 |
IM_ASSERT(v_idx >= 0 && v_idx < values_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8390 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8391 |
const float v0 = values_getter(data, (v_idx + values_offset) % values_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8392 |
const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8393 |
if (plot_type == ImGuiPlotType_Lines) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8394 |
SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8395 |
else if (plot_type == ImGuiPlotType_Histogram) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8396 |
SetTooltip("%d: %8.4g", v_idx, v0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8397 |
idx_hovered = v_idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8398 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8399 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8400 |
const float t_step = 1.0f / (float)res_w; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8401 |
const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8402 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8403 |
float v0 = values_getter(data, (0 + values_offset) % values_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8404 |
float t0 = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8405 |
ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8406 |
float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8407 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8408 |
const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8409 |
const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8410 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8411 |
for (int n = 0; n < res_w; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8412 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8413 |
const float t1 = t0 + t_step; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8414 |
const int v1_idx = (int)(t0 * item_count + 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8415 |
IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8416 |
const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8417 |
const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8418 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8419 |
// NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8420 |
ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8421 |
ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8422 |
if (plot_type == ImGuiPlotType_Lines) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8423 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8424 |
window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8425 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8426 |
else if (plot_type == ImGuiPlotType_Histogram) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8427 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8428 |
if (pos1.x >= pos0.x + 2.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8429 |
pos1.x -= 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8430 |
window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8431 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8432 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8433 |
t0 = t1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8434 |
tp0 = tp1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8435 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8436 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8437 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8438 |
// Text overlay |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8439 |
if (overlay_text) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8440 |
RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8441 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8442 |
if (label_size.x > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8443 |
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8444 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8445 |
// Return hovered index or -1 if none are hovered. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8446 |
// This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8447 |
return idx_hovered; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8448 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8449 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8450 |
struct ImGuiPlotArrayGetterData |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8451 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8452 |
const float* Values; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8453 |
int Stride; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8454 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8455 |
ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8456 |
}; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8457 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8458 |
static float Plot_ArrayGetter(void* data, int idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8459 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8460 |
ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8461 |
const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8462 |
return v; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8463 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8464 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8465 |
void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8466 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8467 |
ImGuiPlotArrayGetterData data(values, stride); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8468 |
PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8469 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8470 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8471 |
void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8472 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8473 |
PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8474 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8475 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8476 |
void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8477 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8478 |
ImGuiPlotArrayGetterData data(values, stride); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8479 |
PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8480 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8481 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8482 |
void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8483 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8484 |
PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8485 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8486 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8487 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8488 |
// [SECTION] Widgets: Value helpers |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8489 |
// Those is not very useful, legacy API. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8490 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8491 |
// - Value() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8492 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8493 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8494 |
void ImGui::Value(const char* prefix, bool b) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8495 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8496 |
Text("%s: %s", prefix, (b ? "true" : "false")); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8497 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8498 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8499 |
void ImGui::Value(const char* prefix, int v) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8500 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8501 |
Text("%s: %d", prefix, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8502 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8503 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8504 |
void ImGui::Value(const char* prefix, unsigned int v) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8505 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8506 |
Text("%s: %d", prefix, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8507 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8508 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8509 |
void ImGui::Value(const char* prefix, float v, const char* float_format) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8510 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8511 |
if (float_format) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8512 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8513 |
char fmt[64]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8514 |
ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8515 |
Text(fmt, prefix, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8516 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8517 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8518 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8519 |
Text("%s: %.3f", prefix, v); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8520 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8521 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8522 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8523 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8524 |
// [SECTION] MenuItem, BeginMenu, EndMenu, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8525 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8526 |
// - ImGuiMenuColumns [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8527 |
// - BeginMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8528 |
// - EndMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8529 |
// - BeginMainMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8530 |
// - EndMainMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8531 |
// - BeginMenu() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8532 |
// - EndMenu() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8533 |
// - MenuItemEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8534 |
// - MenuItem() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8535 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8536 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8537 |
// Helpers for internal use |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8538 |
void ImGuiMenuColumns::Update(float spacing, bool window_reappearing) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8539 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8540 |
if (window_reappearing) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8541 |
memset(Widths, 0, sizeof(Widths)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8542 |
Spacing = (ImU16)spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8543 |
CalcNextTotalWidth(true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8544 |
memset(Widths, 0, sizeof(Widths)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8545 |
TotalWidth = NextTotalWidth; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8546 |
NextTotalWidth = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8547 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8548 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8549 |
void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8550 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8551 |
ImU16 offset = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8552 |
bool want_spacing = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8553 |
for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8554 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8555 |
ImU16 width = Widths[i]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8556 |
if (want_spacing && width > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8557 |
offset += Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8558 |
want_spacing |= (width > 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8559 |
if (update_offsets) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8560 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8561 |
if (i == 1) { OffsetLabel = offset; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8562 |
if (i == 2) { OffsetShortcut = offset; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8563 |
if (i == 3) { OffsetMark = offset; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8564 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8565 |
offset += width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8566 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8567 |
NextTotalWidth = offset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8568 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8569 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8570 |
float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8571 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8572 |
Widths[0] = ImMax(Widths[0], (ImU16)w_icon); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8573 |
Widths[1] = ImMax(Widths[1], (ImU16)w_label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8574 |
Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8575 |
Widths[3] = ImMax(Widths[3], (ImU16)w_mark); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8576 |
CalcNextTotalWidth(false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8577 |
return (float)ImMax(TotalWidth, NextTotalWidth); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8578 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8579 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8580 |
// FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8581 |
// Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8582 |
// Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8583 |
// Then later the same system could be used for multiple menu-bars, scrollbars, side-bars. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8584 |
bool ImGui::BeginMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8585 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8586 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8587 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8588 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8589 |
if (!(window->Flags & ImGuiWindowFlags_MenuBar)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8590 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8591 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8592 |
IM_ASSERT(!window->DC.MenuBarAppending); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8593 |
BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8594 |
PushID("##menubar"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8595 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8596 |
// We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8597 |
// We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8598 |
ImRect bar_rect = window->MenuBarRect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8599 |
ImRect clip_rect(ImFloor(bar_rect.Min.x + window->WindowBorderSize), ImFloor(bar_rect.Min.y + window->WindowBorderSize), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize))), ImFloor(bar_rect.Max.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8600 |
clip_rect.ClipWith(window->OuterRectClipped); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8601 |
PushClipRect(clip_rect.Min, clip_rect.Max, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8602 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8603 |
// We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8604 |
window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8605 |
window->DC.LayoutType = ImGuiLayoutType_Horizontal; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8606 |
window->DC.IsSameLine = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8607 |
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8608 |
window->DC.MenuBarAppending = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8609 |
AlignTextToFramePadding(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8610 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8611 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8612 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8613 |
void ImGui::EndMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8614 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8615 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8616 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8617 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8618 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8619 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8620 |
// Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8621 |
if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8622 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8623 |
// Try to find out if the request is for one of our child menu |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8624 |
ImGuiWindow* nav_earliest_child = g.NavWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8625 |
while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8626 |
nav_earliest_child = nav_earliest_child->ParentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8627 |
if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8628 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8629 |
// To do so we claim focus back, restore NavId and then process the movement request for yet another frame. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8630 |
// This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8631 |
const ImGuiNavLayer layer = ImGuiNavLayer_Menu; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8632 |
IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8633 |
FocusWindow(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8634 |
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8635 |
// FIXME-NAV: How to deal with this when not using g.IO.ConfigNavCursorVisibleAuto? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8636 |
if (g.NavCursorVisible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8637 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8638 |
g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection. Will be set again |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8639 |
g.NavCursorHideFrames = 2; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8640 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8641 |
g.NavHighlightItemUnderNav = g.NavMousePosDirty = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8642 |
NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8643 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8644 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8645 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8646 |
IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8647 |
IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8648 |
IM_ASSERT(window->DC.MenuBarAppending); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8649 |
PopClipRect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8650 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8651 |
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8652 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8653 |
// FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8654 |
ImGuiGroupData& group_data = g.GroupStack.back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8655 |
group_data.EmitItem = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8656 |
ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8657 |
window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8658 |
EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8659 |
window->DC.LayoutType = ImGuiLayoutType_Vertical; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8660 |
window->DC.IsSameLine = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8661 |
window->DC.NavLayerCurrent = ImGuiNavLayer_Main; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8662 |
window->DC.MenuBarAppending = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8663 |
window->DC.CursorMaxPos = restore_cursor_max_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8664 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8665 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8666 |
// Important: calling order matters! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8667 |
// FIXME: Somehow overlapping with docking tech. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8668 |
// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8669 |
bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8670 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8671 |
IM_ASSERT(dir != ImGuiDir_None); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8672 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8673 |
ImGuiWindow* bar_window = FindWindowByName(name); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8674 |
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8675 |
if (bar_window == NULL || bar_window->BeginCount == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8676 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8677 |
// Calculate and set window size/position |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8678 |
ImRect avail_rect = viewport->GetBuildWorkRect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8679 |
ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8680 |
ImVec2 pos = avail_rect.Min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8681 |
if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8682 |
pos[axis] = avail_rect.Max[axis] - axis_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8683 |
ImVec2 size = avail_rect.GetSize(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8684 |
size[axis] = axis_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8685 |
SetNextWindowPos(pos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8686 |
SetNextWindowSize(size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8687 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8688 |
// Report our size into work area (for next frame) using actual window size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8689 |
if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8690 |
viewport->BuildWorkInsetMin[axis] += axis_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8691 |
else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8692 |
viewport->BuildWorkInsetMax[axis] += axis_size; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8693 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8694 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8695 |
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8696 |
SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8697 |
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8698 |
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8699 |
bool is_open = Begin(name, NULL, window_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8700 |
PopStyleVar(2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8701 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8702 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8703 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8704 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8705 |
bool ImGui::BeginMainMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8706 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8707 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8708 |
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8709 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8710 |
// Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8711 |
SetCurrentViewport(NULL, viewport); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8712 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8713 |
// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8714 |
// FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8715 |
// FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8716 |
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8717 |
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8718 |
float height = GetFrameHeight(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8719 |
bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8720 |
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8721 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8722 |
if (is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8723 |
BeginMenuBar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8724 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8725 |
End(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8726 |
return is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8727 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8728 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8729 |
void ImGui::EndMainMenuBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8730 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8731 |
EndMenuBar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8732 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8733 |
// When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8734 |
// FIXME: With this strategy we won't be able to restore a NULL focus. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8735 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8736 |
if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8737 |
FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8738 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8739 |
End(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8740 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8741 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8742 |
static bool IsRootOfOpenMenuSet() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8743 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8744 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8745 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8746 |
if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8747 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8748 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8749 |
// Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8750 |
// (e.g. inside menu bar vs loose menu items) based on parent ID. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8751 |
// This would however prevent the use of e.g. PushID() user code submitting menus. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8752 |
// Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8753 |
// making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8754 |
// Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8755 |
// doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8756 |
// In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8757 |
// This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8758 |
// open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8759 |
// it likely won't be a problem anyone runs into. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8760 |
const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8761 |
if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8762 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8763 |
return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8764 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8765 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8766 |
bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8767 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8768 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8769 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8770 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8771 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8772 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8773 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8774 |
const ImGuiID id = window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8775 |
bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8776 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8777 |
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8778 |
// The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8779 |
ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8780 |
if (window->Flags & ImGuiWindowFlags_ChildMenu) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8781 |
window_flags |= ImGuiWindowFlags_ChildWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8782 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8783 |
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8784 |
// We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8785 |
// If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8786 |
if (g.MenusIdSubmittedThisFrame.contains(id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8787 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8788 |
if (menu_is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8789 |
menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8790 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8791 |
g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8792 |
return menu_is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8793 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8794 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8795 |
// Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8796 |
g.MenusIdSubmittedThisFrame.push_back(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8797 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8798 |
ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8799 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8800 |
// Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8801 |
// This is only done for items for the menu set and not the full parent window. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8802 |
const bool menuset_is_open = IsRootOfOpenMenuSet(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8803 |
if (menuset_is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8804 |
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8805 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8806 |
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8807 |
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8808 |
// e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8809 |
ImVec2 popup_pos, pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8810 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8811 |
if (!enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8812 |
BeginDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8813 |
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8814 |
bool pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8815 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8816 |
// We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8817 |
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8818 |
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8819 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8820 |
// Menu inside an horizontal menu bar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8821 |
// Selectable extend their highlight by half ItemSpacing in each direction. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8822 |
// For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8823 |
popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8824 |
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8825 |
PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8826 |
float w = label_size.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8827 |
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8828 |
pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8829 |
RenderText(text_pos, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8830 |
PopStyleVar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8831 |
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8832 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8833 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8834 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8835 |
// Menu inside a regular/vertical menu |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8836 |
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8837 |
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8838 |
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8839 |
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8840 |
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8841 |
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8842 |
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8843 |
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8844 |
pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8845 |
RenderText(text_pos, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8846 |
if (icon_w > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8847 |
RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8848 |
RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8849 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8850 |
if (!enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8851 |
EndDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8852 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8853 |
const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8854 |
if (menuset_is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8855 |
PopItemFlag(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8856 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8857 |
bool want_open = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8858 |
bool want_open_nav_init = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8859 |
bool want_close = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8860 |
if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8861 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8862 |
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8863 |
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8864 |
bool moving_toward_child_menu = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8865 |
ImGuiPopupData* child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) ? &g.OpenPopupStack[g.BeginPopupStack.Size] : NULL; // Popup candidate (testing below) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8866 |
ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8867 |
if (g.HoveredWindow == window && child_menu_window != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8868 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8869 |
const float ref_unit = g.FontSize; // FIXME-DPI |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8870 |
const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8871 |
const ImRect next_window_rect = child_menu_window->Rect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8872 |
ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8873 |
ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8874 |
ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8875 |
const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // Add a bit of extra slack. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8876 |
ta.x += child_dir * -0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8877 |
tb.x += child_dir * ref_unit; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8878 |
tc.x += child_dir * ref_unit; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8879 |
tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f); // Triangle has maximum height to limit the slope and the bias toward large sub-menus |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8880 |
tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8881 |
moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8882 |
//GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8883 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8884 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8885 |
// The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8886 |
// But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8887 |
// (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8888 |
if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavHighlightItemUnderNav && g.ActiveId == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8889 |
want_close = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8890 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8891 |
// Open |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8892 |
// (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8893 |
if (!menu_is_open && pressed) // Click/activate to open |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8894 |
want_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8895 |
else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8896 |
want_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8897 |
else if (!menu_is_open && hovered && g.HoveredIdTimer >= 0.30f && g.MouseStationaryTimer >= 0.30f) // Hover to open (timer fallback) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8898 |
want_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8899 |
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8900 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8901 |
want_open = want_open_nav_init = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8902 |
NavMoveRequestCancel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8903 |
SetNavCursorVisibleAfterMove(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8904 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8905 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8906 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8907 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8908 |
// Menu bar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8909 |
if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8910 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8911 |
want_close = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8912 |
want_open = menu_is_open = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8913 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8914 |
else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8915 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8916 |
want_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8917 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8918 |
else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8919 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8920 |
want_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8921 |
NavMoveRequestCancel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8922 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8923 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8924 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8925 |
if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8926 |
want_close = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8927 |
if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8928 |
ClosePopupToLevel(g.BeginPopupStack.Size, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8929 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8930 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8931 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8932 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8933 |
if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8934 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8935 |
// Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8936 |
OpenPopup(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8937 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8938 |
else if (want_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8939 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8940 |
menu_is_open = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8941 |
OpenPopup(label, ImGuiPopupFlags_NoReopen);// | (want_open_nav_init ? ImGuiPopupFlags_NoReopenAlwaysNavInit : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8942 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8943 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8944 |
if (menu_is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8945 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8946 |
ImGuiLastItemData last_item_in_parent = g.LastItemData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8947 |
SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8948 |
PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8949 |
menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8950 |
PopStyleVar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8951 |
if (menu_is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8952 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8953 |
// Implement what ImGuiPopupFlags_NoReopenAlwaysNavInit would do: |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8954 |
// Perform an init request in the case the popup was already open (via a previous mouse hover) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8955 |
if (want_open && want_open_nav_init && !g.NavInitRequest) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8956 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8957 |
FocusWindow(g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8958 |
NavInitWindow(g.CurrentWindow, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8959 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8960 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8961 |
// Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8962 |
// (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8963 |
g.LastItemData = last_item_in_parent; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8964 |
if (g.HoveredWindow == window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8965 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8966 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8967 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8968 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8969 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8970 |
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8971 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8972 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8973 |
return menu_is_open; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8974 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8975 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8976 |
bool ImGui::BeginMenu(const char* label, bool enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8977 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8978 |
return BeginMenuEx(label, NULL, enabled); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8979 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8980 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8981 |
void ImGui::EndMenu() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8982 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8983 |
// Nav: When a left move request our menu failed, close ourselves. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8984 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8985 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8986 |
IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginMenu()/EndMenu() calls |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8987 |
ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8988 |
if (window->BeginCount == window->BeginCountPreviousFrame) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8989 |
if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet()) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8990 |
if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8991 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8992 |
ClosePopupToLevel(g.BeginPopupStack.Size - 1, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8993 |
NavMoveRequestCancel(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8994 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8995 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8996 |
EndPopup(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8997 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8998 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
8999 |
bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9000 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9001 |
ImGuiWindow* window = GetCurrentWindow(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9002 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9003 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9004 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9005 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9006 |
ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9007 |
ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9008 |
ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9009 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9010 |
// See BeginMenuEx() for comments about this. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9011 |
const bool menuset_is_open = IsRootOfOpenMenuSet(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9012 |
if (menuset_is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9013 |
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9014 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9015 |
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9016 |
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9017 |
bool pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9018 |
PushID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9019 |
if (!enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9020 |
BeginDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9021 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9022 |
// We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9023 |
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9024 |
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9025 |
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9026 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9027 |
// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9028 |
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9029 |
float w = label_size.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9030 |
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9031 |
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9032 |
PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9033 |
pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9034 |
PopStyleVar(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9035 |
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9036 |
RenderText(text_pos, label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9037 |
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9038 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9039 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9040 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9041 |
// Menu item inside a vertical menu |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9042 |
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9043 |
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9044 |
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9045 |
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9046 |
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9047 |
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9048 |
float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9049 |
pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9050 |
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9051 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9052 |
RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9053 |
if (icon_w > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9054 |
RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9055 |
if (shortcut_w > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9056 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9057 |
PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9058 |
RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9059 |
PopStyleColor(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9060 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9061 |
if (selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9062 |
RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9063 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9064 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9065 |
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9066 |
if (!enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9067 |
EndDisabled(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9068 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9069 |
if (menuset_is_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9070 |
PopItemFlag(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9071 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9072 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9073 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9074 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9075 |
bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9076 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9077 |
return MenuItemEx(label, NULL, shortcut, selected, enabled); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9078 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9079 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9080 |
bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9081 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9082 |
if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9083 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9084 |
if (p_selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9085 |
*p_selected = !*p_selected; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9086 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9087 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9088 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9089 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9090 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9091 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9092 |
// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9093 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9094 |
// - BeginTabBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9095 |
// - BeginTabBarEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9096 |
// - EndTabBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9097 |
// - TabBarLayout() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9098 |
// - TabBarCalcTabID() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9099 |
// - TabBarCalcMaxTabWidth() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9100 |
// - TabBarFindTabById() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9101 |
// - TabBarFindTabByOrder() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9102 |
// - TabBarFindMostRecentlySelectedTabForActiveWindow() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9103 |
// - TabBarGetCurrentTab() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9104 |
// - TabBarGetTabName() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9105 |
// - TabBarAddTab() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9106 |
// - TabBarRemoveTab() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9107 |
// - TabBarCloseTab() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9108 |
// - TabBarScrollClamp() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9109 |
// - TabBarScrollToTab() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9110 |
// - TabBarQueueFocus() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9111 |
// - TabBarQueueReorder() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9112 |
// - TabBarProcessReorderFromMousePos() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9113 |
// - TabBarProcessReorder() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9114 |
// - TabBarScrollingButtons() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9115 |
// - TabBarTabListPopupButton() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9116 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9117 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9118 |
struct ImGuiTabBarSection |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9119 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9120 |
int TabCount; // Number of tabs in this section. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9121 |
float Width; // Sum of width of tabs in this section (after shrinking down) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9122 |
float Spacing; // Horizontal spacing at the end of the section. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9123 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9124 |
ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9125 |
}; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9126 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9127 |
namespace ImGui |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9128 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9129 |
static void TabBarLayout(ImGuiTabBar* tab_bar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9130 |
static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9131 |
static float TabBarCalcMaxTabWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9132 |
static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9133 |
static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9134 |
static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9135 |
static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9136 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9137 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9138 |
ImGuiTabBar::ImGuiTabBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9139 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9140 |
memset(this, 0, sizeof(*this)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9141 |
CurrFrameVisible = PrevFrameVisible = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9142 |
LastTabItemIdx = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9143 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9144 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9145 |
static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9146 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9147 |
return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9148 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9149 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9150 |
static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9151 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9152 |
const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9153 |
const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9154 |
const int a_section = TabItemGetSectionIdx(a); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9155 |
const int b_section = TabItemGetSectionIdx(b); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9156 |
if (a_section != b_section) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9157 |
return a_section - b_section; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9158 |
return (int)(a->IndexDuringLayout - b->IndexDuringLayout); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9159 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9160 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9161 |
static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9162 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9163 |
const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9164 |
const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9165 |
return (int)(a->BeginOrder - b->BeginOrder); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9166 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9167 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9168 |
static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9169 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9170 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9171 |
return ref.Ptr ? (ImGuiTabBar*)ref.Ptr : g.TabBars.GetByIndex(ref.Index); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9172 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9173 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9174 |
static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9175 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9176 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9177 |
if (g.TabBars.Contains(tab_bar)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9178 |
return ImGuiPtrOrIndex(g.TabBars.GetIndex(tab_bar)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9179 |
return ImGuiPtrOrIndex(tab_bar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9180 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9181 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9182 |
bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9183 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9184 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9185 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9186 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9187 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9188 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9189 |
ImGuiID id = window->GetID(str_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9190 |
ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9191 |
ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9192 |
tab_bar->ID = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9193 |
tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9194 |
tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9195 |
//if (g.NavWindow && IsWindowChildOf(g.NavWindow, window, false, false)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9196 |
flags |= ImGuiTabBarFlags_IsFocused; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9197 |
return BeginTabBarEx(tab_bar, tab_bar_bb, flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9198 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9199 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9200 |
bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9201 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9202 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9203 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9204 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9205 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9206 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9207 |
IM_ASSERT(tab_bar->ID != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9208 |
if ((flags & ImGuiTabBarFlags_DockNode) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9209 |
PushOverrideID(tab_bar->ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9210 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9211 |
// Add to stack |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9212 |
g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9213 |
g.CurrentTabBar = tab_bar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9214 |
tab_bar->Window = window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9215 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9216 |
// Append with multiple BeginTabBar()/EndTabBar() pairs. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9217 |
tab_bar->BackupCursorPos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9218 |
if (tab_bar->CurrFrameVisible == g.FrameCount) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9219 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9220 |
window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9221 |
tab_bar->BeginCount++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9222 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9223 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9224 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9225 |
// Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9226 |
if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9227 |
if ((flags & ImGuiTabBarFlags_DockNode) == 0) // FIXME: TabBar with DockNode can now be hybrid |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9228 |
ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9229 |
tab_bar->TabsAddedNew = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9230 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9231 |
// Flags |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9232 |
if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9233 |
flags |= ImGuiTabBarFlags_FittingPolicyDefault_; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9234 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9235 |
tab_bar->Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9236 |
tab_bar->BarRect = tab_bar_bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9237 |
tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9238 |
tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9239 |
tab_bar->CurrFrameVisible = g.FrameCount; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9240 |
tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9241 |
tab_bar->CurrTabsContentsHeight = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9242 |
tab_bar->ItemSpacingY = g.Style.ItemSpacing.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9243 |
tab_bar->FramePadding = g.Style.FramePadding; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9244 |
tab_bar->TabsActiveCount = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9245 |
tab_bar->LastTabItemIdx = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9246 |
tab_bar->BeginCount = 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9247 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9248 |
// Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9249 |
window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9250 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9251 |
// Draw separator |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9252 |
// (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9253 |
const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9254 |
if (g.Style.TabBarBorderSize > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9255 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9256 |
const float y = tab_bar->BarRect.Max.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9257 |
window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9258 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9259 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9260 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9261 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9262 |
void ImGui::EndTabBar() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9263 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9264 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9265 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9266 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9267 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9268 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9269 |
ImGuiTabBar* tab_bar = g.CurrentTabBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9270 |
if (tab_bar == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9271 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9272 |
IM_ASSERT_USER_ERROR(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9273 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9274 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9275 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9276 |
// Fallback in case no TabItem have been submitted |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9277 |
if (tab_bar->WantLayout) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9278 |
TabBarLayout(tab_bar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9279 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9280 |
// Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9281 |
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9282 |
if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9283 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9284 |
tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9285 |
window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9286 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9287 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9288 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9289 |
window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9290 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9291 |
if (tab_bar->BeginCount > 1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9292 |
window->DC.CursorPos = tab_bar->BackupCursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9293 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9294 |
tab_bar->LastTabItemIdx = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9295 |
if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9296 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9297 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9298 |
g.CurrentTabBarStack.pop_back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9299 |
g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9300 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9301 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9302 |
// Scrolling happens only in the central section (leading/trailing sections are not scrolling) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9303 |
static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9304 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9305 |
return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9306 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9307 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9308 |
// This is called only once a frame before by the first call to ItemTab() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9309 |
// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9310 |
static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9311 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9312 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9313 |
tab_bar->WantLayout = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9314 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9315 |
// Garbage collect by compacting list |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9316 |
// Detect if we need to sort out tab list (e.g. in rare case where a tab changed section) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9317 |
int tab_dst_n = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9318 |
bool need_sort_by_section = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9319 |
ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9320 |
for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9321 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9322 |
ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9323 |
if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9324 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9325 |
// Remove tab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9326 |
if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9327 |
if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9328 |
if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9329 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9330 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9331 |
if (tab_dst_n != tab_src_n) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9332 |
tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9333 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9334 |
tab = &tab_bar->Tabs[tab_dst_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9335 |
tab->IndexDuringLayout = (ImS16)tab_dst_n; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9336 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9337 |
// We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9338 |
int curr_tab_section_n = TabItemGetSectionIdx(tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9339 |
if (tab_dst_n > 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9340 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9341 |
ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9342 |
int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9343 |
if (curr_tab_section_n == 0 && prev_tab_section_n != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9344 |
need_sort_by_section = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9345 |
if (prev_tab_section_n == 2 && curr_tab_section_n != 2) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9346 |
need_sort_by_section = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9347 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9348 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9349 |
sections[curr_tab_section_n].TabCount++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9350 |
tab_dst_n++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9351 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9352 |
if (tab_bar->Tabs.Size != tab_dst_n) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9353 |
tab_bar->Tabs.resize(tab_dst_n); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9354 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9355 |
if (need_sort_by_section) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9356 |
ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9357 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9358 |
// Calculate spacing between sections |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9359 |
sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9360 |
sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9361 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9362 |
// Setup next selected tab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9363 |
ImGuiID scroll_to_tab_id = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9364 |
if (tab_bar->NextSelectedTabId) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9365 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9366 |
tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9367 |
tab_bar->NextSelectedTabId = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9368 |
scroll_to_tab_id = tab_bar->SelectedTabId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9369 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9370 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9371 |
// Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9372 |
if (tab_bar->ReorderRequestTabId != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9373 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9374 |
if (TabBarProcessReorder(tab_bar)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9375 |
if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9376 |
scroll_to_tab_id = tab_bar->ReorderRequestTabId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9377 |
tab_bar->ReorderRequestTabId = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9378 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9379 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9380 |
// Tab List Popup (will alter tab_bar->BarRect and therefore the available width!) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9381 |
const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9382 |
if (tab_list_popup_button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9383 |
if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9384 |
scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9385 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9386 |
// Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9387 |
// (whereas our tabs are stored as: leading, central, trailing) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9388 |
int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount }; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9389 |
g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9390 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9391 |
// Compute ideal tabs widths + store them into shrink buffer |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9392 |
ImGuiTabItem* most_recently_selected_tab = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9393 |
int curr_section_n = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9394 |
bool found_selected_tab_id = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9395 |
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9396 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9397 |
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9398 |
IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9399 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9400 |
if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9401 |
most_recently_selected_tab = tab; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9402 |
if (tab->ID == tab_bar->SelectedTabId) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9403 |
found_selected_tab_id = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9404 |
if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9405 |
scroll_to_tab_id = tab->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9406 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9407 |
// Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9408 |
// Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9409 |
// and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9410 |
const char* tab_name = TabBarGetTabName(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9411 |
const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9412 |
tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9413 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9414 |
int section_n = TabItemGetSectionIdx(tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9415 |
ImGuiTabBarSection* section = §ions[section_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9416 |
section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9417 |
curr_section_n = section_n; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9418 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9419 |
// Store data so we can build an array sorted by width if we need to shrink tabs down |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9420 |
IM_MSVC_WARNING_SUPPRESS(6385); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9421 |
ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9422 |
shrink_width_item->Index = tab_n; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9423 |
shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9424 |
tab->Width = ImMax(tab->ContentWidth, 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9425 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9426 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9427 |
// Compute total ideal width (used for e.g. auto-resizing a window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9428 |
tab_bar->WidthAllTabsIdeal = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9429 |
for (int section_n = 0; section_n < 3; section_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9430 |
tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9431 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9432 |
// Horizontal scrolling buttons |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9433 |
// (note that TabBarScrollButtons() will alter BarRect.Max.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9434 |
if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9435 |
if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9436 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9437 |
scroll_to_tab_id = scroll_and_select_tab->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9438 |
if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9439 |
tab_bar->SelectedTabId = scroll_to_tab_id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9440 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9441 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9442 |
// Shrink widths if full tabs don't fit in their allocated space |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9443 |
float section_0_w = sections[0].Width + sections[0].Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9444 |
float section_1_w = sections[1].Width + sections[1].Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9445 |
float section_2_w = sections[2].Width + sections[2].Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9446 |
bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9447 |
float width_excess; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9448 |
if (central_section_is_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9449 |
width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9450 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9451 |
width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9452 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9453 |
// With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9454 |
if (width_excess >= 1.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9455 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9456 |
int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9457 |
int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9458 |
ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9459 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9460 |
// Apply shrunk values into tabs and sections |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9461 |
for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9462 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9463 |
ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9464 |
float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9465 |
if (shrinked_width < 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9466 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9467 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9468 |
shrinked_width = ImMax(1.0f, shrinked_width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9469 |
int section_n = TabItemGetSectionIdx(tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9470 |
sections[section_n].Width -= (tab->Width - shrinked_width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9471 |
tab->Width = shrinked_width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9472 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9473 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9474 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9475 |
// Layout all active tabs |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9476 |
int section_tab_index = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9477 |
float tab_offset = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9478 |
tab_bar->WidthAllTabs = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9479 |
for (int section_n = 0; section_n < 3; section_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9480 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9481 |
ImGuiTabBarSection* section = §ions[section_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9482 |
if (section_n == 2) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9483 |
tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9484 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9485 |
for (int tab_n = 0; tab_n < section->TabCount; tab_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9486 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9487 |
ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9488 |
tab->Offset = tab_offset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9489 |
tab->NameOffset = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9490 |
tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9491 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9492 |
tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9493 |
tab_offset += section->Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9494 |
section_tab_index += section->TabCount; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9495 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9496 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9497 |
// Clear name buffers |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9498 |
tab_bar->TabsNames.Buf.resize(0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9499 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9500 |
// If we have lost the selected tab, select the next most recently active one |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9501 |
if (found_selected_tab_id == false) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9502 |
tab_bar->SelectedTabId = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9503 |
if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9504 |
scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9505 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9506 |
// Lock in visible tab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9507 |
tab_bar->VisibleTabId = tab_bar->SelectedTabId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9508 |
tab_bar->VisibleTabWasSubmitted = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9509 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9510 |
// CTRL+TAB can override visible tab temporarily |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9511 |
if (g.NavWindowingTarget != NULL && g.NavWindowingTarget->DockNode && g.NavWindowingTarget->DockNode->TabBar == tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9512 |
tab_bar->VisibleTabId = scroll_to_tab_id = g.NavWindowingTarget->TabId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9513 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9514 |
// Apply request requests |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9515 |
if (scroll_to_tab_id != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9516 |
TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9517 |
else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9518 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9519 |
const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9520 |
const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9521 |
if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9522 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9523 |
const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9524 |
tab_bar->ScrollingTargetDistToVisibility = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9525 |
tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9526 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9527 |
SetKeyOwner(wheel_key, tab_bar->ID); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9528 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9529 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9530 |
// Update scrolling |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9531 |
tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9532 |
tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9533 |
if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9534 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9535 |
// Scrolling speed adjust itself so we can always reach our target in 1/3 seconds. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9536 |
// Teleport if we are aiming far off the visible line |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9537 |
tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9538 |
tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9539 |
const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9540 |
tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9541 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9542 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9543 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9544 |
tab_bar->ScrollingSpeed = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9545 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9546 |
tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9547 |
tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9548 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9549 |
// Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9550 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9551 |
window->DC.CursorPos = tab_bar->BarRect.Min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9552 |
ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9553 |
window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9554 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9555 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9556 |
// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9557 |
static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9558 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9559 |
if (docked_window != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9560 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9561 |
IM_UNUSED(tab_bar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9562 |
IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9563 |
ImGuiID id = docked_window->TabId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9564 |
KeepAliveID(id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9565 |
return id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9566 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9567 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9568 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9569 |
ImGuiWindow* window = GImGui->CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9570 |
return window->GetID(label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9571 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9572 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9573 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9574 |
static float ImGui::TabBarCalcMaxTabWidth() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9575 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9576 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9577 |
return g.FontSize * 20.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9578 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9579 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9580 |
ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9581 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9582 |
if (tab_id != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9583 |
for (int n = 0; n < tab_bar->Tabs.Size; n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9584 |
if (tab_bar->Tabs[n].ID == tab_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9585 |
return &tab_bar->Tabs[n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9586 |
return NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9587 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9588 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9589 |
// Order = visible order, not submission order! (which is tab->BeginOrder) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9590 |
ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9591 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9592 |
if (order < 0 || order >= tab_bar->Tabs.Size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9593 |
return NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9594 |
return &tab_bar->Tabs[order]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9595 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9596 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9597 |
// FIXME: See references to #2304 in TODO.txt |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9598 |
ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9599 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9600 |
ImGuiTabItem* most_recently_selected_tab = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9601 |
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9602 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9603 |
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9604 |
if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9605 |
if (tab->Window && tab->Window->WasActive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9606 |
most_recently_selected_tab = tab; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9607 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9608 |
return most_recently_selected_tab; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9609 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9610 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9611 |
ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9612 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9613 |
if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9614 |
return NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9615 |
return &tab_bar->Tabs[tab_bar->LastTabItemIdx]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9616 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9617 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9618 |
const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9619 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9620 |
if (tab->Window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9621 |
return tab->Window->Name; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9622 |
if (tab->NameOffset == -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9623 |
return "N/A"; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9624 |
IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9625 |
return tab_bar->TabsNames.Buf.Data + tab->NameOffset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9626 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9627 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9628 |
// The purpose of this call is to register tab in advance so we can control their order at the time they appear. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9629 |
// Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9630 |
void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9631 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9632 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9633 |
IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9634 |
IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9635 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9636 |
if (!window->HasCloseButton) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9637 |
tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9638 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9639 |
ImGuiTabItem new_tab; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9640 |
new_tab.ID = window->TabId; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9641 |
new_tab.Flags = tab_flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9642 |
new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9643 |
if (new_tab.LastFrameVisible == -1) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9644 |
new_tab.LastFrameVisible = g.FrameCount - 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9645 |
new_tab.Window = window; // Required so tab bar layout can compute the tab width before tab submission |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9646 |
tab_bar->Tabs.push_back(new_tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9647 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9648 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9649 |
// The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9650 |
void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9651 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9652 |
if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9653 |
tab_bar->Tabs.erase(tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9654 |
if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9655 |
if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9656 |
if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9657 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9658 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9659 |
// Called on manual closure attempt |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9660 |
void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9661 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9662 |
if (tab->Flags & ImGuiTabItemFlags_Button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9663 |
return; // A button appended with TabItemButton(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9664 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9665 |
if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9666 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9667 |
// This will remove a frame of lag for selecting another tab on closure. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9668 |
// However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9669 |
tab->WantClose = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9670 |
if (tab_bar->VisibleTabId == tab->ID) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9671 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9672 |
tab->LastFrameVisible = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9673 |
tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9674 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9675 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9676 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9677 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9678 |
// Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9679 |
if (tab_bar->VisibleTabId != tab->ID) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9680 |
TabBarQueueFocus(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9681 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9682 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9683 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9684 |
static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9685 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9686 |
scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9687 |
return ImMax(scrolling, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9688 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9689 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9690 |
// Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9691 |
static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9692 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9693 |
ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9694 |
if (tab == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9695 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9696 |
if (tab->Flags & ImGuiTabItemFlags_SectionMask_) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9697 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9698 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9699 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9700 |
float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9701 |
int order = TabBarGetTabOrder(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9702 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9703 |
// Scrolling happens only in the central section (leading/trailing sections are not scrolling) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9704 |
float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9705 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9706 |
// We make all tabs positions all relative Sections[0].Width to make code simpler |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9707 |
float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9708 |
float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9709 |
tab_bar->ScrollingTargetDistToVisibility = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9710 |
if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9711 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9712 |
// Scroll to the left |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9713 |
tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9714 |
tab_bar->ScrollingTarget = tab_x1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9715 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9716 |
else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9717 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9718 |
// Scroll to the right |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9719 |
tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9720 |
tab_bar->ScrollingTarget = tab_x2 - scrollable_width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9721 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9722 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9723 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9724 |
void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9725 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9726 |
tab_bar->NextSelectedTabId = tab->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9727 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9728 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9729 |
void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9730 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9731 |
IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0); // Only supported for manual/explicit tab bars |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9732 |
ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9733 |
tab_bar->NextSelectedTabId = tab_id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9734 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9735 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9736 |
void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9737 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9738 |
IM_ASSERT(offset != 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9739 |
IM_ASSERT(tab_bar->ReorderRequestTabId == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9740 |
tab_bar->ReorderRequestTabId = tab->ID; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9741 |
tab_bar->ReorderRequestOffset = (ImS16)offset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9742 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9743 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9744 |
void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* src_tab, ImVec2 mouse_pos) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9745 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9746 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9747 |
IM_ASSERT(tab_bar->ReorderRequestTabId == 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9748 |
if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9749 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9750 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9751 |
const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9752 |
const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9753 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9754 |
// Count number of contiguous tabs we are crossing over |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9755 |
const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9756 |
const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9757 |
int dst_idx = src_idx; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9758 |
for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9759 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9760 |
// Reordered tabs must share the same section |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9761 |
const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9762 |
if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9763 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9764 |
if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9765 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9766 |
dst_idx = i; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9767 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9768 |
// Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9769 |
const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9770 |
const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9771 |
//GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9772 |
if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9773 |
break; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9774 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9775 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9776 |
if (dst_idx != src_idx) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9777 |
TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9778 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9779 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9780 |
bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9781 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9782 |
ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9783 |
if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9784 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9785 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9786 |
//IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9787 |
int tab2_order = TabBarGetTabOrder(tab_bar, tab1) + tab_bar->ReorderRequestOffset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9788 |
if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9789 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9790 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9791 |
// Reordered tabs must share the same section |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9792 |
// (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9793 |
ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9794 |
if (tab2->Flags & ImGuiTabItemFlags_NoReorder) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9795 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9796 |
if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9797 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9798 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9799 |
ImGuiTabItem item_tmp = *tab1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9800 |
ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9801 |
ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9802 |
const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9803 |
memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9804 |
*tab2 = item_tmp; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9805 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9806 |
if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9807 |
MarkIniSettingsDirty(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9808 |
return true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9809 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9810 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9811 |
static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9812 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9813 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9814 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9815 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9816 |
const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9817 |
const float scrolling_buttons_width = arrow_button_size.x * 2.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9818 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9819 |
const ImVec2 backup_cursor_pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9820 |
//window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9821 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9822 |
int select_dir = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9823 |
ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9824 |
arrow_col.w *= 0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9825 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9826 |
PushStyleColor(ImGuiCol_Text, arrow_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9827 |
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9828 |
PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9829 |
const float backup_repeat_delay = g.IO.KeyRepeatDelay; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9830 |
const float backup_repeat_rate = g.IO.KeyRepeatRate; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9831 |
g.IO.KeyRepeatDelay = 0.250f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9832 |
g.IO.KeyRepeatRate = 0.200f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9833 |
float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9834 |
window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9835 |
if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9836 |
select_dir = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9837 |
window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9838 |
if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9839 |
select_dir = +1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9840 |
PopItemFlag(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9841 |
PopStyleColor(2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9842 |
g.IO.KeyRepeatRate = backup_repeat_rate; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9843 |
g.IO.KeyRepeatDelay = backup_repeat_delay; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9844 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9845 |
ImGuiTabItem* tab_to_scroll_to = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9846 |
if (select_dir != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9847 |
if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9848 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9849 |
int selected_order = TabBarGetTabOrder(tab_bar, tab_item); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9850 |
int target_order = selected_order + select_dir; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9851 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9852 |
// Skip tab item buttons until another tab item is found or end is reached |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9853 |
while (tab_to_scroll_to == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9854 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9855 |
// If we are at the end of the list, still scroll to make our tab visible |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9856 |
tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9857 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9858 |
// Cross through buttons |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9859 |
// (even if first/last item is a button, return it so we can update the scroll) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9860 |
if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9861 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9862 |
target_order += select_dir; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9863 |
selected_order += select_dir; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9864 |
tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9865 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9866 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9867 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9868 |
window->DC.CursorPos = backup_cursor_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9869 |
tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9870 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9871 |
return tab_to_scroll_to; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9872 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9873 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9874 |
static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9875 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9876 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9877 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9878 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9879 |
// We use g.Style.FramePadding.y to match the square ArrowButton size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9880 |
const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9881 |
const ImVec2 backup_cursor_pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9882 |
window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9883 |
tab_bar->BarRect.Min.x += tab_list_popup_button_width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9884 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9885 |
ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9886 |
arrow_col.w *= 0.5f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9887 |
PushStyleColor(ImGuiCol_Text, arrow_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9888 |
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9889 |
bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9890 |
PopStyleColor(2); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9891 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9892 |
ImGuiTabItem* tab_to_select = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9893 |
if (open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9894 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9895 |
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9896 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9897 |
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9898 |
if (tab->Flags & ImGuiTabItemFlags_Button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9899 |
continue; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9900 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9901 |
const char* tab_name = TabBarGetTabName(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9902 |
if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9903 |
tab_to_select = tab; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9904 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9905 |
EndCombo(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9906 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9907 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9908 |
window->DC.CursorPos = backup_cursor_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9909 |
return tab_to_select; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9910 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9911 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9912 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9913 |
// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9914 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9915 |
// - BeginTabItem() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9916 |
// - EndTabItem() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9917 |
// - TabItemButton() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9918 |
// - TabItemEx() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9919 |
// - SetTabItemClosed() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9920 |
// - TabItemCalcSize() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9921 |
// - TabItemBackground() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9922 |
// - TabItemLabelAndCloseButton() [Internal] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9923 |
//------------------------------------------------------------------------- |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9924 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9925 |
bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9926 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9927 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9928 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9929 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9930 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9931 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9932 |
ImGuiTabBar* tab_bar = g.CurrentTabBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9933 |
if (tab_bar == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9934 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9935 |
IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9936 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9937 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9938 |
IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9939 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9940 |
bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9941 |
if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9942 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9943 |
ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9944 |
PushOverrideID(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9945 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9946 |
return ret; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9947 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9948 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9949 |
void ImGui::EndTabItem() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9950 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9951 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9952 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9953 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9954 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9955 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9956 |
ImGuiTabBar* tab_bar = g.CurrentTabBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9957 |
if (tab_bar == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9958 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9959 |
IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9960 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9961 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9962 |
IM_ASSERT(tab_bar->LastTabItemIdx >= 0); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9963 |
ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9964 |
if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9965 |
PopID(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9966 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9967 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9968 |
bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9969 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9970 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9971 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9972 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9973 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9974 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9975 |
ImGuiTabBar* tab_bar = g.CurrentTabBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9976 |
if (tab_bar == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9977 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9978 |
IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9979 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9980 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9981 |
return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9982 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9983 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9984 |
bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9985 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9986 |
// Layout whole tab bar if not already done |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9987 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9988 |
if (tab_bar->WantLayout) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9989 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9990 |
ImGuiNextItemData backup_next_item_data = g.NextItemData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9991 |
TabBarLayout(tab_bar); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9992 |
g.NextItemData = backup_next_item_data; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9993 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9994 |
ImGuiWindow* window = g.CurrentWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9995 |
if (window->SkipItems) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9996 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9997 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9998 |
const ImGuiStyle& style = g.Style; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
9999 |
const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10000 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10001 |
// If the user called us with *p_open == false, we early out and don't render. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10002 |
// We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10003 |
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10004 |
if (p_open && !*p_open) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10005 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10006 |
ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10007 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10008 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10009 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10010 |
IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10011 |
IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10012 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10013 |
// Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10014 |
if (flags & ImGuiTabItemFlags_NoCloseButton) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10015 |
p_open = NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10016 |
else if (p_open == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10017 |
flags |= ImGuiTabItemFlags_NoCloseButton; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10018 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10019 |
// Acquire tab data |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10020 |
ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10021 |
bool tab_is_new = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10022 |
if (tab == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10023 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10024 |
tab_bar->Tabs.push_back(ImGuiTabItem()); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10025 |
tab = &tab_bar->Tabs.back(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10026 |
tab->ID = id; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10027 |
tab_bar->TabsAddedNew = tab_is_new = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10028 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10029 |
tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10030 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10031 |
// Calculate tab contents size |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10032 |
ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10033 |
tab->RequestedWidth = -1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10034 |
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10035 |
size.x = tab->RequestedWidth = g.NextItemData.Width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10036 |
if (tab_is_new) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10037 |
tab->Width = ImMax(1.0f, size.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10038 |
tab->ContentWidth = size.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10039 |
tab->BeginOrder = tab_bar->TabsActiveCount++; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10040 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10041 |
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10042 |
const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10043 |
const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10044 |
const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10045 |
const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10046 |
tab->LastFrameVisible = g.FrameCount; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10047 |
tab->Flags = flags; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10048 |
tab->Window = docked_window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10049 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10050 |
// Append name _WITH_ the zero-terminator |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10051 |
// (regular tabs are permitted in a DockNode tab bar, but window tabs not permitted in a non-DockNode tab bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10052 |
if (docked_window != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10053 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10054 |
IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10055 |
tab->NameOffset = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10056 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10057 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10058 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10059 |
tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10060 |
tab_bar->TabsNames.append(label, label + strlen(label) + 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10061 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10062 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10063 |
// Update selected tab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10064 |
if (!is_tab_button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10065 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10066 |
if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10067 |
if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10068 |
TabBarQueueFocus(tab_bar, tab); // New tabs gets activated |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10069 |
if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // _SetSelected can only be passed on explicit tab bar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10070 |
TabBarQueueFocus(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10071 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10072 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10073 |
// Lock visibility |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10074 |
// (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10075 |
bool tab_contents_visible = (tab_bar->VisibleTabId == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10076 |
if (tab_contents_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10077 |
tab_bar->VisibleTabWasSubmitted = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10078 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10079 |
// On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10080 |
if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing && docked_window == NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10081 |
if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10082 |
tab_contents_visible = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10083 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10084 |
// Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10085 |
// and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10086 |
if (tab_appearing && (!tab_bar_appearing || tab_is_new)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10087 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10088 |
ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10089 |
if (is_tab_button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10090 |
return false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10091 |
return tab_contents_visible; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10092 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10093 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10094 |
if (tab_bar->SelectedTabId == id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10095 |
tab->LastFrameSelected = g.FrameCount; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10096 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10097 |
// Backup current layout position |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10098 |
const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10099 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10100 |
// Layout |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10101 |
const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10102 |
size.x = tab->Width; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10103 |
if (is_central_section) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10104 |
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10105 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10106 |
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10107 |
ImVec2 pos = window->DC.CursorPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10108 |
ImRect bb(pos, pos + size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10109 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10110 |
// We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10111 |
const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10112 |
if (want_clip_rect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10113 |
PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10114 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10115 |
ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10116 |
ItemSize(bb.GetSize(), style.FramePadding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10117 |
window->DC.CursorMaxPos = backup_cursor_max_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10118 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10119 |
if (!ItemAdd(bb, id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10120 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10121 |
if (want_clip_rect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10122 |
PopClipRect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10123 |
window->DC.CursorPos = backup_main_cursor_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10124 |
return tab_contents_visible; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10125 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10126 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10127 |
// Click to Select a tab |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10128 |
ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10129 |
if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10130 |
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10131 |
bool hovered, held; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10132 |
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10133 |
if (pressed && !is_tab_button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10134 |
TabBarQueueFocus(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10135 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10136 |
// Transfer active id window so the active id is not owned by the dock host (as StartMouseMovingWindow() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10137 |
// will only do it on the drag). This allows FocusWindow() to be more conservative in how it clears active id. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10138 |
if (held && docked_window && g.ActiveId == id && g.ActiveIdIsJustActivated) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10139 |
g.ActiveIdWindow = docked_window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10140 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10141 |
// Drag and drop a single floating window node moves it |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10142 |
ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10143 |
const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10144 |
if (held && single_floating_window_node && IsMouseDragging(0, 0.0f)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10145 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10146 |
// Move |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10147 |
StartMouseMovingWindow(docked_window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10148 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10149 |
else if (held && !tab_appearing && IsMouseDragging(0)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10150 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10151 |
// Drag and drop: re-order tabs |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10152 |
int drag_dir = 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10153 |
float drag_distance_from_edge_x = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10154 |
if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL))) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10155 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10156 |
// While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10157 |
if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10158 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10159 |
drag_dir = -1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10160 |
drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10161 |
TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10162 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10163 |
else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10164 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10165 |
drag_dir = +1; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10166 |
drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10167 |
TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10168 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10169 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10170 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10171 |
// Extract a Dockable window out of it's tab bar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10172 |
const bool can_undock = docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove) && !(node->MergedFlags & ImGuiDockNodeFlags_NoUndocking); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10173 |
if (can_undock) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10174 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10175 |
// We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10176 |
bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10177 |
if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10178 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10179 |
float threshold_base = g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10180 |
float threshold_x = (threshold_base * 2.2f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10181 |
float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10182 |
//GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10183 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10184 |
float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10185 |
if (distance_from_edge_y >= threshold_y) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10186 |
undocking_tab = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10187 |
if (drag_distance_from_edge_x > threshold_x) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10188 |
if ((drag_dir < 0 && TabBarGetTabOrder(tab_bar, tab) == 0) || (drag_dir > 0 && TabBarGetTabOrder(tab_bar, tab) == tab_bar->Tabs.Size - 1)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10189 |
undocking_tab = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10190 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10191 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10192 |
if (undocking_tab) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10193 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10194 |
// Undock |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10195 |
// FIXME: refactor to share more code with e.g. StartMouseMovingWindow |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10196 |
DockContextQueueUndockWindow(&g, docked_window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10197 |
g.MovingWindow = docked_window; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10198 |
SetActiveID(g.MovingWindow->MoveId, g.MovingWindow); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10199 |
g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10200 |
g.ActiveIdNoClearOnFocusLoss = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10201 |
SetActiveIdUsingAllKeyboardKeys(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10202 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10203 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10204 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10205 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10206 |
#if 0 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10207 |
if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10208 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10209 |
// Enlarge tab display when hovering |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10210 |
bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f))); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10211 |
display_draw_list = GetForegroundDrawList(window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10212 |
TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10213 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10214 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10215 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10216 |
// Render tab shape |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10217 |
ImDrawList* display_draw_list = window->DrawList; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10218 |
const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10219 |
TabItemBackground(display_draw_list, bb, flags, tab_col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10220 |
if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10221 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10222 |
float x_offset = IM_TRUNC(0.4f * style.TabRounding); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10223 |
if (x_offset < 2.0f * g.CurrentDpiScale) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10224 |
x_offset = 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10225 |
float y_offset = 1.0f * g.CurrentDpiScale; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10226 |
display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), style.TabBarOverlineSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10227 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10228 |
RenderNavCursor(bb, id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10229 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10230 |
// Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10231 |
const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10232 |
if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10233 |
TabBarQueueFocus(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10234 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10235 |
if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10236 |
flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10237 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10238 |
// Render tab label, process close button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10239 |
const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10240 |
bool just_closed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10241 |
bool text_clipped; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10242 |
TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10243 |
if (just_closed && p_open != NULL) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10244 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10245 |
*p_open = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10246 |
TabBarCloseTab(tab_bar, tab); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10247 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10248 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10249 |
// Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10250 |
// That state is copied to window->DockTabItemStatusFlags by our caller. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10251 |
if (docked_window && (hovered || g.HoveredId == close_button_id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10252 |
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10253 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10254 |
// Restore main window position so user can draw there |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10255 |
if (want_clip_rect) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10256 |
PopClipRect(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10257 |
window->DC.CursorPos = backup_main_cursor_pos; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10258 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10259 |
// Tooltip |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10260 |
// (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10261 |
// (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10262 |
// FIXME: This is a mess. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10263 |
// FIXME: We may want disabled tab to still display the tooltip? |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10264 |
if (text_clipped && g.HoveredId == id && !held) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10265 |
if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10266 |
SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10267 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10268 |
IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10269 |
if (is_tab_button) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10270 |
return pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10271 |
return tab_contents_visible; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10272 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10273 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10274 |
// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10275 |
// To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar(). |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10276 |
// Tabs closed by the close button will automatically be flagged to avoid this issue. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10277 |
void ImGui::SetTabItemClosed(const char* label) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10278 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10279 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10280 |
bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10281 |
if (is_within_manual_tab_bar) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10282 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10283 |
ImGuiTabBar* tab_bar = g.CurrentTabBar; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10284 |
ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10285 |
if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10286 |
tab->WantClose = true; // Will be processed by next call to TabBarLayout() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10287 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10288 |
else if (ImGuiWindow* window = FindWindowByName(label)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10289 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10290 |
if (window->DockIsActive) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10291 |
if (ImGuiDockNode* node = window->DockNode) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10292 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10293 |
ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10294 |
TabBarRemoveTab(node->TabBar, tab_id); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10295 |
window->DockTabWantClose = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10296 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10297 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10298 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10299 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10300 |
ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10301 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10302 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10303 |
ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10304 |
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10305 |
if (has_close_button_or_unsaved_marker) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10306 |
size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10307 |
else |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10308 |
size.x += g.Style.FramePadding.x + 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10309 |
return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10310 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10311 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10312 |
ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10313 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10314 |
return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10315 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10316 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10317 |
void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10318 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10319 |
// While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10320 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10321 |
const float width = bb.GetWidth(); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10322 |
IM_UNUSED(flags); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10323 |
IM_ASSERT(width > 0.0f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10324 |
const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10325 |
const float y1 = bb.Min.y + 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10326 |
const float y2 = bb.Max.y - g.Style.TabBarBorderSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10327 |
draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10328 |
draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10329 |
draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10330 |
draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10331 |
draw_list->PathFillConvex(col); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10332 |
if (g.Style.TabBorderSize > 0.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10333 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10334 |
draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10335 |
draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10336 |
draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10337 |
draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10338 |
draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10339 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10340 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10341 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10342 |
// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10343 |
// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10344 |
void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10345 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10346 |
ImGuiContext& g = *GImGui; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10347 |
ImVec2 label_size = CalcTextSize(label, NULL, true); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10348 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10349 |
if (out_just_closed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10350 |
*out_just_closed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10351 |
if (out_text_clipped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10352 |
*out_text_clipped = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10353 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10354 |
if (bb.GetWidth() <= 1.0f) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10355 |
return; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10356 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10357 |
// In Style V2 we'll have full override of all colors per state (e.g. focused, selected) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10358 |
// But right now if you want to alter text color of tabs this is what you need to do. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10359 |
#if 0 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10360 |
const float backup_alpha = g.Style.Alpha; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10361 |
if (!is_contents_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10362 |
g.Style.Alpha *= 0.7f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10363 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10364 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10365 |
// Render text label (with clipping + alpha gradient) + unsaved marker |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10366 |
ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10367 |
ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10368 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10369 |
// Return clipped state ignoring the close button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10370 |
if (out_text_clipped) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10371 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10372 |
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10373 |
//draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10374 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10375 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10376 |
const float button_sz = g.FontSize; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10377 |
const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10378 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10379 |
// Close Button & Unsaved Marker |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10380 |
// We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10381 |
// 'hovered' will be true when hovering the Tab but NOT when hovering the close button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10382 |
// 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10383 |
// 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10384 |
bool close_button_pressed = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10385 |
bool close_button_visible = false; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10386 |
if (close_button_id != 0) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10387 |
if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10388 |
if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10389 |
close_button_visible = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10390 |
bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10391 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10392 |
if (close_button_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10393 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10394 |
ImGuiLastItemData last_item_backup = g.LastItemData; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10395 |
if (CloseButton(close_button_id, button_pos)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10396 |
close_button_pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10397 |
g.LastItemData = last_item_backup; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10398 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10399 |
// Close with middle mouse button |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10400 |
if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10401 |
close_button_pressed = true; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10402 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10403 |
else if (unsaved_marker_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10404 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10405 |
const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10406 |
RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10407 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10408 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10409 |
// This is all rather complicated |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10410 |
// (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10411 |
// FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10412 |
float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10413 |
if (close_button_visible || unsaved_marker_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10414 |
{ |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10415 |
text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10416 |
text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10417 |
ellipsis_max_x = text_pixel_clip_bb.Max.x; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10418 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10419 |
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10420 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10421 |
#if 0 |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10422 |
if (!is_contents_visible) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10423 |
g.Style.Alpha = backup_alpha; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10424 |
#endif |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10425 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10426 |
if (out_just_closed) |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10427 |
*out_just_closed = close_button_pressed; |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10428 |
} |
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10429 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10430 |
|
| 1193b4be |
Poul Sander |
2025-02-03 21:28 |
10431 |
#endif // #ifndef IMGUI_DISABLE |
|
|
1970-01-01 00:00 |
10432 |
|