1// dear imgui, v1.66b
2// (main code and documentation)
3
4// Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5// Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6// Get latest version at https://github.com/ocornut/imgui
7// Releases change-log at https://github.com/ocornut/imgui/releases
8// Technical Support for Getting Started https://discourse.dearimgui.org/c/getting-started
9// Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
10// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
11// This library is free but I need your support to sustain development and maintenance.
12// If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui
13
14// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
15// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
16// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
17// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
18// to a better solution or official support for them.
19
20/*
21
22Index of this file:
23
24DOCUMENTATION
25
26- MISSION STATEMENT
27- END-USER GUIDE
28- PROGRAMMER GUIDE (read me!)
29 - Read first
30 - How to update to a newer version of Dear ImGui
31 - Getting started with integrating Dear ImGui in your code/engine
32 - This is how a simple application may look like (2 variations)
33 - This is how a simple rendering function may look like
34 - Using gamepad/keyboard navigation controls
35- API BREAKING CHANGES (read me when you update!)
36- FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
37 - How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
38 - How can I display an image? What is ImTextureID, how does it works?
39 - How can I have multiple widgets with the same label or without a label? A primer on labels and the ID Stack.
40 - How can I use my own math types instead of ImVec2/ImVec4?
41 - How can I load a different font than the default?
42 - How can I easily use icons in my application?
43 - How can I load multiple fonts?
44 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
45 - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
46 - I integrated Dear ImGui in my engine and the text or lines are blurry..
47 - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
48 - How can I help?
49
50CODE
51(search for "[SECTION]" in the code to find them)
52
53// [SECTION] FORWARD DECLARATIONS
54// [SECTION] CONTEXT AND MEMORY ALLOCATORS
55// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
56// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
57// [SECTION] MISC HELPER/UTILITIES (ImText* functions)
58// [SECTION] MISC HELPER/UTILITIES (Color functions)
59// [SECTION] ImGuiStorage
60// [SECTION] ImGuiTextFilter
61// [SECTION] ImGuiTextBuffer
62// [SECTION] ImGuiListClipper
63// [SECTION] RENDER HELPERS
64// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
65// [SECTION] TOOLTIPS
66// [SECTION] POPUPS
67// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
68// [SECTION] COLUMNS
69// [SECTION] DRAG AND DROP
70// [SECTION] LOGGING/CAPTURING
71// [SECTION] SETTINGS
72// [SECTION] PLATFORM DEPENDENT HELPERS
73// [SECTION] METRICS/DEBUG WINDOW
74
75*/
76
77//-----------------------------------------------------------------------------
78// DOCUMENTATION
79//-----------------------------------------------------------------------------
80
81/*
82
83 MISSION STATEMENT
84 =================
85
86 - Easy to use to create code-driven and data-driven tools
87 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
88 - Easy to hack and improve
89 - Minimize screen real-estate usage
90 - Minimize setup and maintenance
91 - Minimize state storage on user side
92 - Portable, minimize dependencies, run on target (consoles, phones, etc.)
93 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,
94 opening a tree node for the first time, etc. but a typical frame should not allocate anything)
95
96 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
97 - Doesn't look fancy, doesn't animate
98 - Limited layout features, intricate layouts are typically crafted in code
99
100
101 END-USER GUIDE
102 ==============
103
104 - Double-click on title bar to collapse window.
105 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
106 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
107 - Click and drag on any empty space to move window.
108 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
109 - CTRL+Click on a slider or drag box to input value as text.
110 - Use mouse wheel to scroll.
111 - Text editor:
112 - Hold SHIFT or use mouse to select text.
113 - CTRL+Left/Right to word jump.
114 - CTRL+Shift+Left/Right to select words.
115 - CTRL+A our Double-Click to select all.
116 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
117 - CTRL+Z,CTRL+Y to undo/redo.
118 - ESCAPE to revert text to its original value.
119 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
120 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
121 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
122 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
123
124
125 PROGRAMMER GUIDE
126 ================
127
128 READ FIRST
129
130 - Read the FAQ below this section!
131 - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction
132 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
133 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
134 - You can learn about immediate-mode GUI principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
135 See README.md for more links describing the IMGUI paradigm. Dear ImGui is an implementation of the IMGUI paradigm.
136
137 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
138
139 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
140 - Or maintain your own branch where you have imconfig.h modified.
141 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
142 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
143 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
144 likely be a comment about it. Please report any issue to the GitHub page!
145 - Try to keep your copy of dear imgui reasonably up to date.
146
147 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
148
149 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
150 - Add the Dear ImGui source files to your projects or using your preferred build system.
151 It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
152 - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating imgui types with your own maths types.
153 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
154 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
155 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
156 phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
157 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
158 - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.
159
160 HOW A SIMPLE APPLICATION MAY LOOK LIKE
161 EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder)
162
163 // Application init: create a dear imgui context, setup some options, load fonts
164 ImGui::CreateContext();
165 ImGuiIO& io = ImGui::GetIO();
166 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
167 // TODO: Fill optional fields of the io structure later.
168 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
169
170 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
171 ImGui_ImplWin32_Init(hwnd);
172 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
173
174 // Application main loop
175 while (true)
176 {
177 // Feed inputs to dear imgui, start new frame
178 ImGui_ImplDX11_NewFrame();
179 ImGui_ImplWin32_NewFrame();
180 ImGui::NewFrame();
181
182 // Any application code here
183 ImGui::Text("Hello, world!");
184
185 // Render dear imgui into screen
186 ImGui::Render();
187 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
188 g_pSwapChain->Present(1, 0);
189 }
190
191 // Shutdown
192 ImGui_ImplDX11_Shutdown();
193 ImGui_ImplWin32_Shutdown();
194 ImGui::DestroyContext();
195
196 HOW A SIMPLE APPLICATION MAY LOOK LIKE
197 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
198
199 // Application init: create a dear imgui context, setup some options, load fonts
200 ImGui::CreateContext();
201 ImGuiIO& io = ImGui::GetIO();
202 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
203 // TODO: Fill optional fields of the io structure later.
204 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
205
206 // Build and load the texture atlas into a texture
207 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
208 int width, height;
209 unsigned char* pixels = NULL;
210 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
211
212 // At this point you've got the texture data and you need to upload that your your graphic system:
213 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
214 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
215 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
216 io.Fonts->TexID = (void*)texture;
217
218 // Application main loop
219 while (true)
220 {
221 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
222 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
223 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
224 io.DisplaySize.x = 1920.0f; // set the current display width
225 io.DisplaySize.y = 1280.0f; // set the current display height here
226 io.MousePos = my_mouse_pos; // set the mouse position
227 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
228 io.MouseDown[1] = my_mouse_buttons[1];
229
230 // Call NewFrame(), after this point you can use ImGui::* functions anytime
231 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
232 ImGui::NewFrame();
233
234 // Most of your application code here
235 ImGui::Text("Hello, world!");
236 MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
237 MyGameRender(); // may use any ImGui functions as well!
238
239 // Render imgui, swap buffers
240 // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
241 ImGui::EndFrame();
242 ImGui::Render();
243 ImDrawData* draw_data = ImGui::GetDrawData();
244 MyImGuiRenderFunction(draw_data);
245 SwapBuffers();
246 }
247
248 // Shutdown
249 ImGui::DestroyContext();
250
251 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
252
253 void void MyImGuiRenderFunction(ImDrawData* draw_data)
254 {
255 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
256 // TODO: Setup viewport using draw_data->DisplaySize
257 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
258 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
259 for (int n = 0; n < draw_data->CmdListsCount; n++)
260 {
261 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui
262 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui
263 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
264 {
265 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
266 if (pcmd->UserCallback)
267 {
268 pcmd->UserCallback(cmd_list, pcmd);
269 }
270 else
271 {
272 // The texture for the draw call is specified by pcmd->TextureId.
273 // The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization.
274 MyEngineBindTexture((MyTexture*)pcmd->TextureId);
275
276 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
277 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
278 // (some elements visible outside their bounds) but you can fix that once everything else works!
279 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
280 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
281 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
282 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
283 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
284 ImVec2 pos = draw_data->DisplayPos;
285 MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y));
286
287 // Render 'pcmd->ElemCount/3' indexed triangles.
288 // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
289 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
290 }
291 idx_buffer += pcmd->ElemCount;
292 }
293 }
294 }
295
296 - The examples/ folders contains many actual implementation of the pseudo-codes above.
297 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
298 They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs
299 from the rest of your application. In every cases you need to pass on the inputs to imgui. Refer to the FAQ for more information.
300 - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues!
301
302 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
303
304 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
305 - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
306 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
307 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
308 - Gamepad:
309 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
310 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
311 Note that io.NavInputs[] is cleared by EndFrame().
312 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
313 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
314 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
315 Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
316 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
317 - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo
318 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
319 - Keyboard:
320 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
321 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
322 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
323 will be set. For more advanced uses, you may want to read from:
324 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
325 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
326 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
327 Please reach out if you think the game vs navigation input sharing could be improved.
328 - Mouse:
329 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
330 - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard.
331 - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.
332 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
333 When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
334 When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
335 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
336 (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want
337 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
338
339
340 API BREAKING CHANGES
341 ====================
342
343 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
344 Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.
345 When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
346 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
347
348 - 2018/10/12 (1.66) - Renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
349 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
350 - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h.
351 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
352 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
353 - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp.
354 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
355 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
356 - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent).
357 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
358 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
359 - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature.
360 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
361 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
362 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
363 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
364 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
365 - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more.
366 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
367 To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.
368 If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them.
369 - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format",
370 consistent with other functions. Kept redirection functions (will obsolete).
371 - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.
372 - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
373 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
374 - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
375 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
376 - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.
377 - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.
378 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
379 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
380 - removed Shutdown() function, as DestroyContext() serve this purpose.
381 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
382 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
383 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
384 - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.
385 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
386 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
387 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
388 - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.
389 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
390 - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags
391 - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.
392 - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.
393 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
394 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
395 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
396 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
397 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
398 - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.
399 - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
400 Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
401 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
402 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
403 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
404 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
405 - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
406 - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
407 - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
408 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
409 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
410 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
411 - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
412 - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
413 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
414 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
415 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
416 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
417 - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
418 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
419 - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
420 - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
421 - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
422 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
423 - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
424 - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
425 - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))'
426 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
427 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
428 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
429 - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
430 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
431 - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
432 - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
433 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
434 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
435 If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
436 This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
437 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
438 {
439 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
440 return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a);
441 }
442 If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
443 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
444 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
445 - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
446 - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
447 - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337).
448 - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
449 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
450 - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
451 - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
452 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
453 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
454 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
455 GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
456 GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
457 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
458 - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
459 - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
460 - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
461 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
462 - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
463 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
464 - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
465 - the signature of the io.RenderDrawListsFn handler has changed!
466 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
467 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
468 argument: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
469 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
470 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
471 - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
472 - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
473 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
474 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
475 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
476 - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
477 - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
478 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
479 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
480 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
481 - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
482 - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
483 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
484 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
485 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
486 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
487 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
488 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
489 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
490 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
491 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
492 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
493 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
494 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
495 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
496 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
497 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
498 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
499 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
500 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
501 font init: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>
502 became: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier;
503 you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
504 it is now recommended that you sample the font texture with bilinear interpolation.
505 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
506 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
507 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
508 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
509 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
510 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
511 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
512 - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
513 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
514 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
515 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
516 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
517 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
518 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
519
520
521 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
522 ======================================
523
524 Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
525 A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
526 - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
527 - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
528 - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
529 Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
530 This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
531 Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
532 It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
533 Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
534 perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
535 Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
536 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
537 were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
538
539 Q: How can I display an image? What is ImTextureID, how does it works?
540 A: Short explanation:
541 - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures.
542 - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value.
543 - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
544 Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
545
546 Long explanation:
547 - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
548 At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code
549 to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.).
550 - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
551 We carry the information to identify a "texture" in the ImTextureID type.
552 ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice.
553 Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
554 - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
555 an image from the end-user perspective. This is what the _examples_ rendering functions are using:
556
557 OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
558 DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp)
559 DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp)
560 DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp)
561
562 For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
563 Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
564 tying together both the texture and information about its format and how to read it.
565 - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about
566 the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase
567 is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
568 If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID
569 representation suggested by the example bindings is probably the best choice.
570 (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
571
572 User code may do:
573
574 // Cast our texture type to ImTextureID / void*
575 MyTexture* texture = g_CoffeeTableTexture;
576 ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
577
578 The renderer function called after ImGui::Render() will receive that same value that the user code passed:
579
580 // Cast ImTextureID / void* stored in the draw command as our texture type
581 MyTexture* texture = (MyTexture*)pcmd->TextureId;
582 MyEngineBindTexture2D(texture);
583
584 Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
585 This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them.
586 If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using.
587
588 Here's a simplified OpenGL example using stb_image.h:
589
590 // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
591 #define STB_IMAGE_IMPLEMENTATION
592 #include <stb_image.h>
593 [...]
594 int my_image_width, my_image_height;
595 unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
596
597 // Turn the RGBA pixel data into an OpenGL texture:
598 GLuint my_opengl_texture;
599 glGenTextures(1, &my_opengl_texture);
600 glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
601 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
602 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
603 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
604 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
605
606 // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it:
607 ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
608
609 C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa.
610 Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
611 Examples:
612
613 GLuint my_tex = XXX;
614 void* my_void_ptr;
615 my_void_ptr = (void*)(intptr_t)my_tex; // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer)
616 my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint
617
618 ID3D11ShaderResourceView* my_dx11_srv = XXX;
619 void* my_void_ptr;
620 my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void*
621 my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView*
622
623 Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
624
625 Q: How can I have multiple widgets with the same label or without a label?
626 Q: I have multiple widgets with the same label, and only the first one works. Why is that?
627 A: A primer on labels and the ID Stack...
628
629 Dear ImGui internally need to uniquely identify UI elements.
630 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
631 Interactive widgets (such as calls to Button buttons) need a unique ID.
632 Unique ID are used internally to track active widgets and occasionally associate state to widgets.
633 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
634
635 - Unique ID are often derived from a string label:
636
637 Button("OK"); // Label = "OK", ID = hash of (..., "OK")
638 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
639
640 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
641 two buttons labeled "OK" in different windows or different tree locations is fine.
642 We used "..." above to signify whatever was already pushed to the ID stack previously:
643
644 Begin("MyWindow");
645 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
646 End();
647
648 - If you have a same ID twice in the same location, you'll have a conflict:
649
650 Button("OK");
651 Button("OK"); // ID collision! Interacting with either button will trigger the first one.
652
653 Fear not! this is easy to solve and there are many ways to solve it!
654
655 - Solving ID conflict in a simple/local context:
656 When passing a label you can optionally specify extra ID information within string itself.
657 Use "##" to pass a complement to the ID that won't be visible to the end-user.
658 This helps solving the simple collision cases when you know e.g. at compilation time which items
659 are going to be created:
660
661 Begin("MyWindow");
662 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
663 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
664 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
665 End();
666
667 - If you want to completely hide the label, but still need an ID:
668
669 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox!
670
671 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
672 you to animate labels. For example you may want to include varying information in a window title bar,
673 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
674
675 Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "ID")
676 Button("World###ID"); // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different
677
678 sprintf(buf, "My game (%f FPS)###MyGame", fps);
679 Begin(buf); // Variable title, ID = hash of "MyGame"
680
681 - Solving ID conflict in a more general manner:
682 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
683 within the same window. This is the most convenient way of distinguishing ID when iterating and
684 creating many UI elements programmatically.
685 You can push a pointer, a string or an integer value into the ID stack.
686 Remember that ID are formed from the concatenation of _everything_ in the ID stack!
687
688 Begin("Window");
689 for (int i = 0; i < 100; i++)
690 {
691 PushID(i); // Push i to the id tack
692 Button("Click"); // Label = "Click", ID = Hash of ("Window", i, "Click")
693 PopID();
694 }
695 for (int i = 0; i < 100; i++)
696 {
697 MyObject* obj = Objects[i];
698 PushID(obj);
699 Button("Click"); // Label = "Click", ID = Hash of ("Window", obj pointer, "Click")
700 PopID();
701 }
702 for (int i = 0; i < 100; i++)
703 {
704 MyObject* obj = Objects[i];
705 PushID(obj->Name);
706 Button("Click"); // Label = "Click", ID = Hash of ("Window", obj->Name, "Click")
707 PopID();
708 }
709 End();
710
711 - More example showing that you can stack multiple prefixes into the ID stack:
712
713 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
714 PushID("node");
715 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
716 PushID(my_ptr);
717 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
718 PopID();
719 PopID();
720
721 - Tree nodes implicitly creates a scope for you by calling PushID().
722
723 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
724 if (TreeNode("node"))
725 {
726 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
727 TreePop();
728 }
729
730 - When working with trees, ID are used to preserve the open/close state of each tree node.
731 Depending on your use cases you may want to use strings, indices or pointers as ID.
732 e.g. when following a single pointer that may change over time, using a static string as ID
733 will preserve your node open/closed state when the targeted object change.
734 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
735 node open/closed state differently. See what makes more sense in your situation!
736
737 Q: How can I use my own math types instead of ImVec2/ImVec4?
738 A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
739 This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
740
741 Q: How can I load a different font than the default?
742 A: Use the font atlas to load the TTF/OTF file you want:
743 ImGuiIO& io = ImGui::GetIO();
744 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
745 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
746 Default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code.
747 (Read the 'misc/fonts/README.txt' file for more details about font loading.)
748
749 New programmers: remember that in C/C++ and most programming languages if you want to use a
750 backslash \ within a string literal, you need to write it double backslash "\\":
751 io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!)
752 io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT
753 io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT
754
755 Q: How can I easily use icons in my application?
756 A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
757 main font. Then you can refer to icons within your strings.
758 (Read the 'misc/fonts/README.txt' file for more details about icons font loading.)
759
760 Q: How can I load multiple fonts?
761 A: Use the font atlas to pack them into a single texture:
762 (Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.)
763
764 ImGuiIO& io = ImGui::GetIO();
765 ImFont* font0 = io.Fonts->AddFontDefault();
766 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
767 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
768 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
769 // the first loaded font gets used by default
770 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
771
772 // Options
773 ImFontConfig config;
774 config.OversampleH = 3;
775 config.OversampleV = 1;
776 config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up
777 config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
778 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config);
779
780 // Combine multiple fonts into one (e.g. for icon fonts)
781 static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
782 ImFontConfig config;
783 config.MergeMode = true;
784 io.Fonts->AddFontDefault();
785 io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
786 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
787
788 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
789 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
790
791 // Add default Japanese ranges
792 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
793
794 // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
795 ImVector<ImWchar> ranges;
796 ImFontAtlas::GlyphRangesBuilder builder;
797 builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
798 builder.AddChar(0x7262); // Add a specific character
799 builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
800 builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
801 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
802
803 All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
804 by using the u8"hello" syntax. Specifying literal in your source code using a local code page
805 (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
806 Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
807
808 Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
809 The applications in examples/ are doing that.
810 Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
811 You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
812 Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for
813 the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
814
815 Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
816 A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags.
817 (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse)
818 Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
819 - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
820 - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData,
821 and then call your rendered code with your own ImDrawList or ImDrawData data.
822
823 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
824 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
825 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
826
827 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
828 A: You are probably mishandling the clipping rectangles in your render function.
829 Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
830
831 Q: How can I help?
832 A: - If you are experienced with Dear ImGui and C++, look at the github issues, or docs/TODO.txt and see how you want/can help!
833 - Convince your company to sponsor/fund development! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README.
834 - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
835 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers.
836 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
837 - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately).
838
839 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
840 this is also useful to set yourself in the context of another window (to get/set other settings)
841 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
842 - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
843 of a deep nested inner loop in your code.
844 - tip: you can call Render() multiple times (e.g for VR renders).
845 - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
846
847*/
848
849#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
850#define _CRT_SECURE_NO_WARNINGS
851#endif
852
853#include "imgui.h"
854#ifndef IMGUI_DEFINE_MATH_OPERATORS
855#define IMGUI_DEFINE_MATH_OPERATORS
856#endif
857#include "imgui_internal.h"
858
859#include <ctype.h> // toupper, isprint
860#include <stdio.h> // vsnprintf, sscanf, printf
861#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
862#include <stddef.h> // intptr_t
863#else
864#include <stdint.h> // intptr_t
865#endif
866
867// Debug options
868#define IMGUI_DEBUG_NAV_SCORING 0
869#define IMGUI_DEBUG_NAV_RECTS 0
870
871// Visual Studio warnings
872#ifdef _MSC_VER
873#pragma warning (disable: 4127) // condition expression is constant
874#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
875#endif
876
877// Clang/GCC warnings with -Weverything
878#ifdef __clang__
879#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
880#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
881#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.
882#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.
883#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
884#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
885#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
886#pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
887#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
888#elif defined(__GNUC__)
889#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
890#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
891#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
892#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
893#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
894#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
895#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
896#if __GNUC__ >= 8
897#pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
898#endif
899#endif
900
901// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.
902static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
903static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
904
905// Window resizing from edges (when io.ConfigResizeWindowsFromEdges = true)
906static const float RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
907static const float RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
908
909//-------------------------------------------------------------------------
910// [SECTION] FORWARD DECLARATIONS
911//-------------------------------------------------------------------------
912
913static void SetCurrentWindow(ImGuiWindow* window);
914static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
915static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
916static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
917static void FindHoveredWindow();
918static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
919static void CheckStacksSize(ImGuiWindow* window, bool write);
920static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
921
922static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
923static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
924static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
925
926static ImRect GetViewportRect();
927
928// Settings
929static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
930static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
931static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
932
933// Platform Dependents default implementation for IO functions
934static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
935static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
936static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
937
938namespace ImGui
939{
940static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
941
942// Navigation
943static void NavUpdate();
944static void NavUpdateWindowing();
945static void NavUpdateWindowingList();
946static void NavUpdateMoveResult();
947static float NavUpdatePageUpPageDown(int allowed_dir_flags);
948static inline void NavUpdateAnyRequestFlag();
949static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
950static ImVec2 NavCalcPreferredRefPos();
951static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
952static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
953
954// Misc
955static void UpdateMouseInputs();
956static void UpdateMouseWheel();
957static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
958}
959
960// Test engine hooks (imgui-test)
961//#define IMGUI_ENABLE_TEST_ENGINE_HOOKS
962#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
963extern void ImGuiTestEngineHook_PreNewFrame();
964extern void ImGuiTestEngineHook_PostNewFrame();
965extern void ImGuiTestEngineHook_ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg);
966#endif
967
968//-----------------------------------------------------------------------------
969// [SECTION] CONTEXT AND MEMORY ALLOCATORS
970//-----------------------------------------------------------------------------
971
972// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
973// CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
974// If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
975// ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can:
976// - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
977// - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
978#ifndef GImGui
979ImGuiContext* GImGui = NULL;
980#endif
981
982// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
983// If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
984// Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
985#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
986static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; return malloc(size: size); }
987static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; free(ptr: ptr); }
988#else
989static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; (void)size; IM_ASSERT(0); return NULL; }
990static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; (void)ptr; IM_ASSERT(0); }
991#endif
992
993static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
994static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
995static void* GImAllocatorUserData = NULL;
996
997//-----------------------------------------------------------------------------
998// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
999//-----------------------------------------------------------------------------
1000
1001ImGuiStyle::ImGuiStyle()
1002{
1003 Alpha = 1.0f; // Global alpha applies to everything in ImGui
1004 WindowPadding = ImVec2(8,8); // Padding within a window
1005 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
1006 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1007 WindowMinSize = ImVec2(32,32); // Minimum window size
1008 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1009 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1010 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1011 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1012 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1013 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1014 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1015 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1016 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1017 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1018 TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
1019 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1020 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
1021 ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1022 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1023 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
1024 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1025 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1026 DisplayWindowPadding = ImVec2(20,20); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
1027 DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
1028 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1029 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1030 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1031 CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
1032
1033 // Default theme
1034 ImGui::StyleColorsDark(dst: this);
1035}
1036
1037// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
1038// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
1039void ImGuiStyle::ScaleAllSizes(float scale_factor)
1040{
1041 WindowPadding = ImFloor(v: WindowPadding * scale_factor);
1042 WindowRounding = ImFloor(f: WindowRounding * scale_factor);
1043 WindowMinSize = ImFloor(v: WindowMinSize * scale_factor);
1044 ChildRounding = ImFloor(f: ChildRounding * scale_factor);
1045 PopupRounding = ImFloor(f: PopupRounding * scale_factor);
1046 FramePadding = ImFloor(v: FramePadding * scale_factor);
1047 FrameRounding = ImFloor(f: FrameRounding * scale_factor);
1048 ItemSpacing = ImFloor(v: ItemSpacing * scale_factor);
1049 ItemInnerSpacing = ImFloor(v: ItemInnerSpacing * scale_factor);
1050 TouchExtraPadding = ImFloor(v: TouchExtraPadding * scale_factor);
1051 IndentSpacing = ImFloor(f: IndentSpacing * scale_factor);
1052 ColumnsMinSpacing = ImFloor(f: ColumnsMinSpacing * scale_factor);
1053 ScrollbarSize = ImFloor(f: ScrollbarSize * scale_factor);
1054 ScrollbarRounding = ImFloor(f: ScrollbarRounding * scale_factor);
1055 GrabMinSize = ImFloor(f: GrabMinSize * scale_factor);
1056 GrabRounding = ImFloor(f: GrabRounding * scale_factor);
1057 DisplayWindowPadding = ImFloor(v: DisplayWindowPadding * scale_factor);
1058 DisplaySafeAreaPadding = ImFloor(v: DisplaySafeAreaPadding * scale_factor);
1059 MouseCursorScale = ImFloor(f: MouseCursorScale * scale_factor);
1060}
1061
1062ImGuiIO::ImGuiIO()
1063{
1064 // Most fields are initialized with zero
1065 memset(s: this, c: 0, n: sizeof(*this));
1066
1067 // Settings
1068 ConfigFlags = ImGuiConfigFlags_None;
1069 BackendFlags = ImGuiBackendFlags_None;
1070 DisplaySize = ImVec2(-1.0f, -1.0f);
1071 DeltaTime = 1.0f/60.0f;
1072 IniSavingRate = 5.0f;
1073 IniFilename = "imgui.ini";
1074 LogFilename = "imgui_log.txt";
1075 MouseDoubleClickTime = 0.30f;
1076 MouseDoubleClickMaxDist = 6.0f;
1077 for (int i = 0; i < ImGuiKey_COUNT; i++)
1078 KeyMap[i] = -1;
1079 KeyRepeatDelay = 0.250f;
1080 KeyRepeatRate = 0.050f;
1081 UserData = NULL;
1082
1083 Fonts = NULL;
1084 FontGlobalScale = 1.0f;
1085 FontDefault = NULL;
1086 FontAllowUserScaling = false;
1087 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1088 DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
1089
1090 // Miscellaneous configuration options
1091#ifdef __APPLE__
1092 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1093#else
1094 ConfigMacOSXBehaviors = false;
1095#endif
1096 ConfigInputTextCursorBlink = true;
1097 ConfigResizeWindowsFromEdges = false;
1098
1099 // Platform Functions
1100 BackendPlatformName = BackendRendererName = NULL;
1101 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1102 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1103 ClipboardUserData = NULL;
1104 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1105 ImeWindowHandle = NULL;
1106
1107#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1108 RenderDrawListsFn = NULL;
1109#endif
1110
1111 // Input (NB: we already have memset zero the entire structure!)
1112 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1113 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1114 MouseDragThreshold = 6.0f;
1115 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1116 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1117 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1118}
1119
1120// Pass in translated ASCII characters for text input.
1121// - with glfw you can get those from the callback set in glfwSetCharCallback()
1122// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1123void ImGuiIO::AddInputCharacter(ImWchar c)
1124{
1125 const int n = ImStrlenW(str: InputCharacters);
1126 if (n + 1 < IM_ARRAYSIZE(InputCharacters))
1127 {
1128 InputCharacters[n] = c;
1129 InputCharacters[n+1] = '\0';
1130 }
1131}
1132
1133void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1134{
1135 // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
1136 const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
1137 ImWchar wchars[wchars_buf_len];
1138 ImTextStrFromUtf8(buf: wchars, buf_size: wchars_buf_len, in_text: utf8_chars, NULL);
1139 for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
1140 AddInputCharacter(c: wchars[i]);
1141}
1142
1143//-----------------------------------------------------------------------------
1144// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1145//-----------------------------------------------------------------------------
1146
1147ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1148{
1149 ImVec2 ap = p - a;
1150 ImVec2 ab_dir = b - a;
1151 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1152 if (dot < 0.0f)
1153 return a;
1154 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1155 if (dot > ab_len_sqr)
1156 return b;
1157 return a + ab_dir * dot / ab_len_sqr;
1158}
1159
1160bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1161{
1162 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1163 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1164 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1165 return ((b1 == b2) && (b2 == b3));
1166}
1167
1168void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1169{
1170 ImVec2 v0 = b - a;
1171 ImVec2 v1 = c - a;
1172 ImVec2 v2 = p - a;
1173 const float denom = v0.x * v1.y - v1.x * v0.y;
1174 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1175 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1176 out_u = 1.0f - out_v - out_w;
1177}
1178
1179ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1180{
1181 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1182 ImVec2 proj_bc = ImLineClosestPoint(a: b, b: c, p);
1183 ImVec2 proj_ca = ImLineClosestPoint(a: c, b: a, p);
1184 float dist2_ab = ImLengthSqr(lhs: p - proj_ab);
1185 float dist2_bc = ImLengthSqr(lhs: p - proj_bc);
1186 float dist2_ca = ImLengthSqr(lhs: p - proj_ca);
1187 float m = ImMin(lhs: dist2_ab, rhs: ImMin(lhs: dist2_bc, rhs: dist2_ca));
1188 if (m == dist2_ab)
1189 return proj_ab;
1190 if (m == dist2_bc)
1191 return proj_bc;
1192 return proj_ca;
1193}
1194
1195int ImStricmp(const char* str1, const char* str2)
1196{
1197 int d;
1198 while ((d = toupper(c: *str2) - toupper(c: *str1)) == 0 && *str1) { str1++; str2++; }
1199 return d;
1200}
1201
1202int ImStrnicmp(const char* str1, const char* str2, size_t count)
1203{
1204 int d = 0;
1205 while (count > 0 && (d = toupper(c: *str2) - toupper(c: *str1)) == 0 && *str1) { str1++; str2++; count--; }
1206 return d;
1207}
1208
1209void ImStrncpy(char* dst, const char* src, size_t count)
1210{
1211 if (count < 1) return;
1212 strncpy(dest: dst, src: src, n: count);
1213 dst[count-1] = 0;
1214}
1215
1216char* ImStrdup(const char *str)
1217{
1218 size_t len = strlen(s: str) + 1;
1219 void* buf = ImGui::MemAlloc(size: len);
1220 return (char*)memcpy(dest: buf, src: (const void*)str, n: len);
1221}
1222
1223const char* ImStrchrRange(const char* str, const char* str_end, char c)
1224{
1225 const char* p = (const char*)memchr(s: str, c: (int)c, n: str_end - str);
1226 return p;
1227}
1228
1229int ImStrlenW(const ImWchar* str)
1230{
1231 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bits
1232 int n = 0;
1233 while (*str++) n++;
1234 return n;
1235}
1236
1237// Find end-of-line. Return pointer will point to either first \n, either str_end.
1238const char* ImStreolRange(const char* str, const char* str_end)
1239{
1240 const char* p = (const char*)memchr(s: str, c: '\n', n: str_end - str);
1241 return p ? p : str_end;
1242}
1243
1244const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1245{
1246 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1247 buf_mid_line--;
1248 return buf_mid_line;
1249}
1250
1251const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1252{
1253 if (!needle_end)
1254 needle_end = needle + strlen(s: needle);
1255
1256 const char un0 = (char)toupper(c: *needle);
1257 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1258 {
1259 if (toupper(c: *haystack) == un0)
1260 {
1261 const char* b = needle + 1;
1262 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1263 if (toupper(c: *a) != toupper(c: *b))
1264 break;
1265 if (b == needle_end)
1266 return haystack;
1267 }
1268 haystack++;
1269 }
1270 return NULL;
1271}
1272
1273// Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
1274void ImStrTrimBlanks(char* buf)
1275{
1276 char* p = buf;
1277 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1278 p++;
1279 char* p_start = p;
1280 while (*p != 0) // Find end of string
1281 p++;
1282 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1283 p--;
1284 if (p_start != buf) // Copy memory if we had leading blanks
1285 memmove(dest: buf, src: p_start, n: p - p_start);
1286 buf[p - p_start] = 0; // Zero terminate
1287}
1288
1289// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1290// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
1291// B) When buf==NULL vsnprintf() will return the output size.
1292#ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1293
1294#if defined(_MSC_VER) && !defined(vsnprintf)
1295#define vsnprintf _vsnprintf
1296#endif
1297
1298int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1299{
1300 va_list args;
1301 va_start(args, fmt);
1302 int w = vsnprintf(s: buf, maxlen: buf_size, format: fmt, arg: args);
1303 va_end(args);
1304 if (buf == NULL)
1305 return w;
1306 if (w == -1 || w >= (int)buf_size)
1307 w = (int)buf_size - 1;
1308 buf[w] = 0;
1309 return w;
1310}
1311
1312int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1313{
1314 int w = vsnprintf(s: buf, maxlen: buf_size, format: fmt, arg: args);
1315 if (buf == NULL)
1316 return w;
1317 if (w == -1 || w >= (int)buf_size)
1318 w = (int)buf_size - 1;
1319 buf[w] = 0;
1320 return w;
1321}
1322#endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1323
1324// Pass data_size==0 for zero-terminated strings
1325// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1326ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1327{
1328 static ImU32 crc32_lut[256] = { 0 };
1329 if (!crc32_lut[1])
1330 {
1331 const ImU32 polynomial = 0xEDB88320;
1332 for (ImU32 i = 0; i < 256; i++)
1333 {
1334 ImU32 crc = i;
1335 for (ImU32 j = 0; j < 8; j++)
1336 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1337 crc32_lut[i] = crc;
1338 }
1339 }
1340
1341 seed = ~seed;
1342 ImU32 crc = seed;
1343 const unsigned char* current = (const unsigned char*)data;
1344
1345 if (data_size > 0)
1346 {
1347 // Known size
1348 while (data_size--)
1349 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1350 }
1351 else
1352 {
1353 // Zero-terminated string
1354 while (unsigned char c = *current++)
1355 {
1356 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1357 // Because this syntax is rarely used we are optimizing for the common case.
1358 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1359 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1360 if (c == '#' && current[0] == '#' && current[1] == '#')
1361 crc = seed;
1362 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1363 }
1364 }
1365 return ~crc;
1366}
1367
1368FILE* ImFileOpen(const char* filename, const char* mode)
1369{
1370#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1371 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1372 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1373 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1374 ImVector<ImWchar> buf;
1375 buf.resize(filename_wsize + mode_wsize);
1376 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1377 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1378 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1379#else
1380 return fopen(filename: filename, modes: mode);
1381#endif
1382}
1383
1384// Load file content into memory
1385// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
1386void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1387{
1388 IM_ASSERT(filename && file_open_mode);
1389 if (out_file_size)
1390 *out_file_size = 0;
1391
1392 FILE* f;
1393 if ((f = ImFileOpen(filename, mode: file_open_mode)) == NULL)
1394 return NULL;
1395
1396 long file_size_signed;
1397 if (fseek(stream: f, off: 0, SEEK_END) || (file_size_signed = ftell(stream: f)) == -1 || fseek(stream: f, off: 0, SEEK_SET))
1398 {
1399 fclose(stream: f);
1400 return NULL;
1401 }
1402
1403 size_t file_size = (size_t)file_size_signed;
1404 void* file_data = ImGui::MemAlloc(size: file_size + padding_bytes);
1405 if (file_data == NULL)
1406 {
1407 fclose(stream: f);
1408 return NULL;
1409 }
1410 if (fread(ptr: file_data, size: 1, n: file_size, stream: f) != file_size)
1411 {
1412 fclose(stream: f);
1413 ImGui::MemFree(ptr: file_data);
1414 return NULL;
1415 }
1416 if (padding_bytes > 0)
1417 memset(s: (void*)(((char*)file_data) + file_size), c: 0, n: (size_t)padding_bytes);
1418
1419 fclose(stream: f);
1420 if (out_file_size)
1421 *out_file_size = file_size;
1422
1423 return file_data;
1424}
1425
1426//-----------------------------------------------------------------------------
1427// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1428//-----------------------------------------------------------------------------
1429
1430// Convert UTF-8 to 32-bits character, process single character input.
1431// Based on stb_from_utf8() from github.com/nothings/stb/
1432// We handle UTF-8 decoding error by skipping forward.
1433int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1434{
1435 unsigned int c = (unsigned int)-1;
1436 const unsigned char* str = (const unsigned char*)in_text;
1437 if (!(*str & 0x80))
1438 {
1439 c = (unsigned int)(*str++);
1440 *out_char = c;
1441 return 1;
1442 }
1443 if ((*str & 0xe0) == 0xc0)
1444 {
1445 *out_char = 0xFFFD; // will be invalid but not end of string
1446 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1447 if (*str < 0xc2) return 2;
1448 c = (unsigned int)((*str++ & 0x1f) << 6);
1449 if ((*str & 0xc0) != 0x80) return 2;
1450 c += (*str++ & 0x3f);
1451 *out_char = c;
1452 return 2;
1453 }
1454 if ((*str & 0xf0) == 0xe0)
1455 {
1456 *out_char = 0xFFFD; // will be invalid but not end of string
1457 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1458 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1459 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1460 c = (unsigned int)((*str++ & 0x0f) << 12);
1461 if ((*str & 0xc0) != 0x80) return 3;
1462 c += (unsigned int)((*str++ & 0x3f) << 6);
1463 if ((*str & 0xc0) != 0x80) return 3;
1464 c += (*str++ & 0x3f);
1465 *out_char = c;
1466 return 3;
1467 }
1468 if ((*str & 0xf8) == 0xf0)
1469 {
1470 *out_char = 0xFFFD; // will be invalid but not end of string
1471 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1472 if (*str > 0xf4) return 4;
1473 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1474 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1475 c = (unsigned int)((*str++ & 0x07) << 18);
1476 if ((*str & 0xc0) != 0x80) return 4;
1477 c += (unsigned int)((*str++ & 0x3f) << 12);
1478 if ((*str & 0xc0) != 0x80) return 4;
1479 c += (unsigned int)((*str++ & 0x3f) << 6);
1480 if ((*str & 0xc0) != 0x80) return 4;
1481 c += (*str++ & 0x3f);
1482 // utf-8 encodings of values used in surrogate pairs are invalid
1483 if ((c & 0xFFFFF800) == 0xD800) return 4;
1484 *out_char = c;
1485 return 4;
1486 }
1487 *out_char = 0;
1488 return 0;
1489}
1490
1491int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1492{
1493 ImWchar* buf_out = buf;
1494 ImWchar* buf_end = buf + buf_size;
1495 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1496 {
1497 unsigned int c;
1498 in_text += ImTextCharFromUtf8(out_char: &c, in_text, in_text_end);
1499 if (c == 0)
1500 break;
1501 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1502 *buf_out++ = (ImWchar)c;
1503 }
1504 *buf_out = 0;
1505 if (in_text_remaining)
1506 *in_text_remaining = in_text;
1507 return (int)(buf_out - buf);
1508}
1509
1510int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1511{
1512 int char_count = 0;
1513 while ((!in_text_end || in_text < in_text_end) && *in_text)
1514 {
1515 unsigned int c;
1516 in_text += ImTextCharFromUtf8(out_char: &c, in_text, in_text_end);
1517 if (c == 0)
1518 break;
1519 if (c < 0x10000)
1520 char_count++;
1521 }
1522 return char_count;
1523}
1524
1525// Based on stb_to_utf8() from github.com/nothings/stb/
1526static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1527{
1528 if (c < 0x80)
1529 {
1530 buf[0] = (char)c;
1531 return 1;
1532 }
1533 if (c < 0x800)
1534 {
1535 if (buf_size < 2) return 0;
1536 buf[0] = (char)(0xc0 + (c >> 6));
1537 buf[1] = (char)(0x80 + (c & 0x3f));
1538 return 2;
1539 }
1540 if (c >= 0xdc00 && c < 0xe000)
1541 {
1542 return 0;
1543 }
1544 if (c >= 0xd800 && c < 0xdc00)
1545 {
1546 if (buf_size < 4) return 0;
1547 buf[0] = (char)(0xf0 + (c >> 18));
1548 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1549 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1550 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1551 return 4;
1552 }
1553 //else if (c < 0x10000)
1554 {
1555 if (buf_size < 3) return 0;
1556 buf[0] = (char)(0xe0 + (c >> 12));
1557 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1558 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1559 return 3;
1560 }
1561}
1562
1563// Not optimal but we very rarely use this function.
1564int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1565{
1566 unsigned int dummy = 0;
1567 return ImTextCharFromUtf8(out_char: &dummy, in_text, in_text_end);
1568}
1569
1570static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1571{
1572 if (c < 0x80) return 1;
1573 if (c < 0x800) return 2;
1574 if (c >= 0xdc00 && c < 0xe000) return 0;
1575 if (c >= 0xd800 && c < 0xdc00) return 4;
1576 return 3;
1577}
1578
1579int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1580{
1581 char* buf_out = buf;
1582 const char* buf_end = buf + buf_size;
1583 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1584 {
1585 unsigned int c = (unsigned int)(*in_text++);
1586 if (c < 0x80)
1587 *buf_out++ = (char)c;
1588 else
1589 buf_out += ImTextCharToUtf8(buf: buf_out, buf_size: (int)(buf_end-buf_out-1), c);
1590 }
1591 *buf_out = 0;
1592 return (int)(buf_out - buf);
1593}
1594
1595int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1596{
1597 int bytes_count = 0;
1598 while ((!in_text_end || in_text < in_text_end) && *in_text)
1599 {
1600 unsigned int c = (unsigned int)(*in_text++);
1601 if (c < 0x80)
1602 bytes_count++;
1603 else
1604 bytes_count += ImTextCountUtf8BytesFromChar(c);
1605 }
1606 return bytes_count;
1607}
1608
1609//-----------------------------------------------------------------------------
1610// [SECTION] MISC HELPER/UTILTIES (Color functions)
1611// Note: The Convert functions are early design which are not consistent with other API.
1612//-----------------------------------------------------------------------------
1613
1614ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1615{
1616 float s = 1.0f/255.0f;
1617 return ImVec4(
1618 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1619 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1620 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1621 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1622}
1623
1624ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1625{
1626 ImU32 out;
1627 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1628 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1629 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1630 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1631 return out;
1632}
1633
1634// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1635// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
1636void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1637{
1638 float K = 0.f;
1639 if (g < b)
1640 {
1641 ImSwap(a&: g, b);
1642 K = -1.f;
1643 }
1644 if (r < g)
1645 {
1646 ImSwap(a&: r, b&: g);
1647 K = -2.f / 6.f - K;
1648 }
1649
1650 const float chroma = r - (g < b ? g : b);
1651 out_h = ImFabs(x: K + (g - b) / (6.f * chroma + 1e-20f));
1652 out_s = chroma / (r + 1e-20f);
1653 out_v = r;
1654}
1655
1656// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1657// also http://en.wikipedia.org/wiki/HSL_and_HSV
1658void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1659{
1660 if (s == 0.0f)
1661 {
1662 // gray
1663 out_r = out_g = out_b = v;
1664 return;
1665 }
1666
1667 h = ImFmod(x: h, y: 1.0f) / (60.0f/360.0f);
1668 int i = (int)h;
1669 float f = h - (float)i;
1670 float p = v * (1.0f - s);
1671 float q = v * (1.0f - s * f);
1672 float t = v * (1.0f - s * (1.0f - f));
1673
1674 switch (i)
1675 {
1676 case 0: out_r = v; out_g = t; out_b = p; break;
1677 case 1: out_r = q; out_g = v; out_b = p; break;
1678 case 2: out_r = p; out_g = v; out_b = t; break;
1679 case 3: out_r = p; out_g = q; out_b = v; break;
1680 case 4: out_r = t; out_g = p; out_b = v; break;
1681 case 5: default: out_r = v; out_g = p; out_b = q; break;
1682 }
1683}
1684
1685ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1686{
1687 ImGuiStyle& style = GImGui->Style;
1688 ImVec4 c = style.Colors[idx];
1689 c.w *= style.Alpha * alpha_mul;
1690 return ColorConvertFloat4ToU32(in: c);
1691}
1692
1693ImU32 ImGui::GetColorU32(const ImVec4& col)
1694{
1695 ImGuiStyle& style = GImGui->Style;
1696 ImVec4 c = col;
1697 c.w *= style.Alpha;
1698 return ColorConvertFloat4ToU32(in: c);
1699}
1700
1701const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1702{
1703 ImGuiStyle& style = GImGui->Style;
1704 return style.Colors[idx];
1705}
1706
1707ImU32 ImGui::GetColorU32(ImU32 col)
1708{
1709 float style_alpha = GImGui->Style.Alpha;
1710 if (style_alpha >= 1.0f)
1711 return col;
1712 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1713 a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1714 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1715}
1716
1717//-----------------------------------------------------------------------------
1718// [SECTION] ImGuiStorage
1719// Helper: Key->value storage
1720//-----------------------------------------------------------------------------
1721
1722// std::lower_bound but without the bullshit
1723static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1724{
1725 ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1726 ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1727 size_t count = (size_t)(last - first);
1728 while (count > 0)
1729 {
1730 size_t count2 = count >> 1;
1731 ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1732 if (mid->key < key)
1733 {
1734 first = ++mid;
1735 count -= count2 + 1;
1736 }
1737 else
1738 {
1739 count = count2;
1740 }
1741 }
1742 return first;
1743}
1744
1745// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
1746void ImGuiStorage::BuildSortByKey()
1747{
1748 struct StaticFunc
1749 {
1750 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1751 {
1752 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1753 if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1754 if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1755 return 0;
1756 }
1757 };
1758 if (Data.Size > 1)
1759 ImQsort(base: Data.Data, nmemb: (size_t)Data.Size, size: sizeof(Pair), compar: StaticFunc::PairCompareByID);
1760}
1761
1762int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1763{
1764 ImVector<Pair>::iterator it = LowerBound(data&: const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1765 if (it == Data.end() || it->key != key)
1766 return default_val;
1767 return it->val_i;
1768}
1769
1770bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1771{
1772 return GetInt(key, default_val: default_val ? 1 : 0) != 0;
1773}
1774
1775float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1776{
1777 ImVector<Pair>::iterator it = LowerBound(data&: const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1778 if (it == Data.end() || it->key != key)
1779 return default_val;
1780 return it->val_f;
1781}
1782
1783void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1784{
1785 ImVector<Pair>::iterator it = LowerBound(data&: const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1786 if (it == Data.end() || it->key != key)
1787 return NULL;
1788 return it->val_p;
1789}
1790
1791// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
1792int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1793{
1794 ImVector<Pair>::iterator it = LowerBound(data&: Data, key);
1795 if (it == Data.end() || it->key != key)
1796 it = Data.insert(it, v: Pair(key, default_val));
1797 return &it->val_i;
1798}
1799
1800bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1801{
1802 return (bool*)GetIntRef(key, default_val: default_val ? 1 : 0);
1803}
1804
1805float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1806{
1807 ImVector<Pair>::iterator it = LowerBound(data&: Data, key);
1808 if (it == Data.end() || it->key != key)
1809 it = Data.insert(it, v: Pair(key, default_val));
1810 return &it->val_f;
1811}
1812
1813void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1814{
1815 ImVector<Pair>::iterator it = LowerBound(data&: Data, key);
1816 if (it == Data.end() || it->key != key)
1817 it = Data.insert(it, v: Pair(key, default_val));
1818 return &it->val_p;
1819}
1820
1821// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
1822void ImGuiStorage::SetInt(ImGuiID key, int val)
1823{
1824 ImVector<Pair>::iterator it = LowerBound(data&: Data, key);
1825 if (it == Data.end() || it->key != key)
1826 {
1827 Data.insert(it, v: Pair(key, val));
1828 return;
1829 }
1830 it->val_i = val;
1831}
1832
1833void ImGuiStorage::SetBool(ImGuiID key, bool val)
1834{
1835 SetInt(key, val: val ? 1 : 0);
1836}
1837
1838void ImGuiStorage::SetFloat(ImGuiID key, float val)
1839{
1840 ImVector<Pair>::iterator it = LowerBound(data&: Data, key);
1841 if (it == Data.end() || it->key != key)
1842 {
1843 Data.insert(it, v: Pair(key, val));
1844 return;
1845 }
1846 it->val_f = val;
1847}
1848
1849void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1850{
1851 ImVector<Pair>::iterator it = LowerBound(data&: Data, key);
1852 if (it == Data.end() || it->key != key)
1853 {
1854 Data.insert(it, v: Pair(key, val));
1855 return;
1856 }
1857 it->val_p = val;
1858}
1859
1860void ImGuiStorage::SetAllInt(int v)
1861{
1862 for (int i = 0; i < Data.Size; i++)
1863 Data[i].val_i = v;
1864}
1865
1866//-----------------------------------------------------------------------------
1867// [SECTION] ImGuiTextFilter
1868//-----------------------------------------------------------------------------
1869
1870// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
1871ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1872{
1873 if (default_filter)
1874 {
1875 ImStrncpy(dst: InputBuf, src: default_filter, IM_ARRAYSIZE(InputBuf));
1876 Build();
1877 }
1878 else
1879 {
1880 InputBuf[0] = 0;
1881 CountGrep = 0;
1882 }
1883}
1884
1885bool ImGuiTextFilter::Draw(const char* label, float width)
1886{
1887 if (width != 0.0f)
1888 ImGui::PushItemWidth(item_width: width);
1889 bool value_changed = ImGui::InputText(label, buf: InputBuf, IM_ARRAYSIZE(InputBuf));
1890 if (width != 0.0f)
1891 ImGui::PopItemWidth();
1892 if (value_changed)
1893 Build();
1894 return value_changed;
1895}
1896
1897void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
1898{
1899 out->resize(new_size: 0);
1900 const char* wb = b;
1901 const char* we = wb;
1902 while (we < e)
1903 {
1904 if (*we == separator)
1905 {
1906 out->push_back(v: TextRange(wb, we));
1907 wb = we + 1;
1908 }
1909 we++;
1910 }
1911 if (wb != we)
1912 out->push_back(v: TextRange(wb, we));
1913}
1914
1915void ImGuiTextFilter::Build()
1916{
1917 Filters.resize(new_size: 0);
1918 TextRange input_range(InputBuf, InputBuf+strlen(s: InputBuf));
1919 input_range.split(separator: ',', out: &Filters);
1920
1921 CountGrep = 0;
1922 for (int i = 0; i != Filters.Size; i++)
1923 {
1924 TextRange& f = Filters[i];
1925 while (f.b < f.e && ImCharIsBlankA(c: f.b[0]))
1926 f.b++;
1927 while (f.e > f.b && ImCharIsBlankA(c: f.e[-1]))
1928 f.e--;
1929 if (f.empty())
1930 continue;
1931 if (Filters[i].b[0] != '-')
1932 CountGrep += 1;
1933 }
1934}
1935
1936bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1937{
1938 if (Filters.empty())
1939 return true;
1940
1941 if (text == NULL)
1942 text = "";
1943
1944 for (int i = 0; i != Filters.Size; i++)
1945 {
1946 const TextRange& f = Filters[i];
1947 if (f.empty())
1948 continue;
1949 if (f.b[0] == '-')
1950 {
1951 // Subtract
1952 if (ImStristr(haystack: text, haystack_end: text_end, needle: f.begin()+1, needle_end: f.end()) != NULL)
1953 return false;
1954 }
1955 else
1956 {
1957 // Grep
1958 if (ImStristr(haystack: text, haystack_end: text_end, needle: f.begin(), needle_end: f.end()) != NULL)
1959 return true;
1960 }
1961 }
1962
1963 // Implicit * grep
1964 if (CountGrep == 0)
1965 return true;
1966
1967 return false;
1968}
1969
1970//-----------------------------------------------------------------------------
1971// [SECTION] ImGuiTextBuffer
1972//-----------------------------------------------------------------------------
1973
1974// On some platform vsnprintf() takes va_list by reference and modifies it.
1975// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1976#ifndef va_copy
1977#if defined(__GNUC__) || defined(__clang__)
1978#define va_copy(dest, src) __builtin_va_copy(dest, src)
1979#else
1980#define va_copy(dest, src) (dest = src)
1981#endif
1982#endif
1983
1984char ImGuiTextBuffer::EmptyString[1] = { 0 };
1985
1986// Helper: Text buffer for logging/accumulating text
1987void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
1988{
1989 va_list args_copy;
1990 va_copy(args_copy, args);
1991
1992 int len = ImFormatStringV(NULL, buf_size: 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1993 if (len <= 0)
1994 {
1995 va_end(args_copy);
1996 return;
1997 }
1998
1999 // Add zero-terminator the first time
2000 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2001 const int needed_sz = write_off + len;
2002 if (write_off + len >= Buf.Capacity)
2003 {
2004 int double_capacity = Buf.Capacity * 2;
2005 Buf.reserve(new_capacity: needed_sz > double_capacity ? needed_sz : double_capacity);
2006 }
2007
2008 Buf.resize(new_size: needed_sz);
2009 ImFormatStringV(buf: &Buf[write_off - 1], buf_size: (size_t)len + 1, fmt, args: args_copy);
2010 va_end(args_copy);
2011}
2012
2013void ImGuiTextBuffer::appendf(const char* fmt, ...)
2014{
2015 va_list args;
2016 va_start(args, fmt);
2017 appendfv(fmt, args);
2018 va_end(args);
2019}
2020
2021//-----------------------------------------------------------------------------
2022// [SECTION] ImGuiListClipper
2023// This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
2024//-----------------------------------------------------------------------------
2025
2026static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2027{
2028 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2029 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2030 // The clipper should probably have a 4th step to display the last item in a regular manner.
2031 ImGui::SetCursorPosY(pos_y);
2032 ImGuiWindow* window = ImGui::GetCurrentWindow();
2033 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.
2034 window->DC.PrevLineSize.y = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
2035 if (window->DC.ColumnsSet)
2036 window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2037}
2038
2039// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2040// Use case B: Begin() called from constructor with items_height>0
2041// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
2042void ImGuiListClipper::Begin(int count, float items_height)
2043{
2044 StartPosY = ImGui::GetCursorPosY();
2045 ItemsHeight = items_height;
2046 ItemsCount = count;
2047 StepNo = 0;
2048 DisplayEnd = DisplayStart = -1;
2049 if (ItemsHeight > 0.0f)
2050 {
2051 ImGui::CalcListClipping(items_count: ItemsCount, items_height: ItemsHeight, out_items_display_start: &DisplayStart, out_items_display_end: &DisplayEnd); // calculate how many to clip/display
2052 if (DisplayStart > 0)
2053 SetCursorPosYAndSetupDummyPrevLine(pos_y: StartPosY + DisplayStart * ItemsHeight, line_height: ItemsHeight); // advance cursor
2054 StepNo = 2;
2055 }
2056}
2057
2058void ImGuiListClipper::End()
2059{
2060 if (ItemsCount < 0)
2061 return;
2062 // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
2063 if (ItemsCount < INT_MAX)
2064 SetCursorPosYAndSetupDummyPrevLine(pos_y: StartPosY + ItemsCount * ItemsHeight, line_height: ItemsHeight); // advance cursor
2065 ItemsCount = -1;
2066 StepNo = 3;
2067}
2068
2069bool ImGuiListClipper::Step()
2070{
2071 if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2072 {
2073 ItemsCount = -1;
2074 return false;
2075 }
2076 if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
2077 {
2078 DisplayStart = 0;
2079 DisplayEnd = 1;
2080 StartPosY = ImGui::GetCursorPosY();
2081 StepNo = 1;
2082 return true;
2083 }
2084 if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
2085 {
2086 if (ItemsCount == 1) { ItemsCount = -1; return false; }
2087 float items_height = ImGui::GetCursorPosY() - StartPosY;
2088 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2089 Begin(count: ItemsCount-1, items_height);
2090 DisplayStart++;
2091 DisplayEnd++;
2092 StepNo = 3;
2093 return true;
2094 }
2095 if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2096 {
2097 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2098 StepNo = 3;
2099 return true;
2100 }
2101 if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
2102 End();
2103 return false;
2104}
2105
2106//-----------------------------------------------------------------------------
2107// [SECTION] RENDER HELPERS
2108// Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2109// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2110//-----------------------------------------------------------------------------
2111
2112const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2113{
2114 const char* text_display_end = text;
2115 if (!text_end)
2116 text_end = (const char*)-1;
2117
2118 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2119 text_display_end++;
2120 return text_display_end;
2121}
2122
2123// Internal ImGui functions to render text
2124// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
2125void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2126{
2127 ImGuiContext& g = *GImGui;
2128 ImGuiWindow* window = g.CurrentWindow;
2129
2130 // Hide anything after a '##' string
2131 const char* text_display_end;
2132 if (hide_text_after_hash)
2133 {
2134 text_display_end = FindRenderedTextEnd(text, text_end);
2135 }
2136 else
2137 {
2138 if (!text_end)
2139 text_end = text + strlen(s: text); // FIXME-OPT
2140 text_display_end = text_end;
2141 }
2142
2143 if (text != text_display_end)
2144 {
2145 window->DrawList->AddText(font: g.Font, font_size: g.FontSize, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end: text_display_end);
2146 if (g.LogEnabled)
2147 LogRenderedText(ref_pos: &pos, text, text_end: text_display_end);
2148 }
2149}
2150
2151void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2152{
2153 ImGuiContext& g = *GImGui;
2154 ImGuiWindow* window = g.CurrentWindow;
2155
2156 if (!text_end)
2157 text_end = text + strlen(s: text); // FIXME-OPT
2158
2159 if (text != text_end)
2160 {
2161 window->DrawList->AddText(font: g.Font, font_size: g.FontSize, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end, wrap_width);
2162 if (g.LogEnabled)
2163 LogRenderedText(ref_pos: &pos, text, text_end);
2164 }
2165}
2166
2167// Default clip_rect uses (pos_min,pos_max)
2168// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
2169void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2170{
2171 // Hide anything after a '##' string
2172 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2173 const int text_len = (int)(text_display_end - text);
2174 if (text_len == 0)
2175 return;
2176
2177 ImGuiContext& g = *GImGui;
2178 ImGuiWindow* window = g.CurrentWindow;
2179
2180 // Perform CPU side clipping for single clipped element to avoid using scissor state
2181 ImVec2 pos = pos_min;
2182 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end: text_display_end, hide_text_after_double_hash: false, wrap_width: 0.0f);
2183
2184 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2185 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2186 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2187 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2188 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2189
2190 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2191 if (align.x > 0.0f) pos.x = ImMax(lhs: pos.x, rhs: pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2192 if (align.y > 0.0f) pos.y = ImMax(lhs: pos.y, rhs: pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2193
2194 // Render
2195 if (need_clipping)
2196 {
2197 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2198 window->DrawList->AddText(font: g.Font, font_size: g.FontSize, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end: text_display_end, wrap_width: 0.0f, cpu_fine_clip_rect: &fine_clip_rect);
2199 }
2200 else
2201 {
2202 window->DrawList->AddText(font: g.Font, font_size: g.FontSize, pos, col: GetColorU32(idx: ImGuiCol_Text), text_begin: text, text_end: text_display_end, wrap_width: 0.0f, NULL);
2203 }
2204 if (g.LogEnabled)
2205 LogRenderedText(ref_pos: &pos, text, text_end: text_display_end);
2206}
2207
2208// Render a rectangle shaped with optional rounding and borders
2209void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2210{
2211 ImGuiContext& g = *GImGui;
2212 ImGuiWindow* window = g.CurrentWindow;
2213 window->DrawList->AddRectFilled(a: p_min, b: p_max, col: fill_col, rounding);
2214 const float border_size = g.Style.FrameBorderSize;
2215 if (border && border_size > 0.0f)
2216 {
2217 window->DrawList->AddRect(a: p_min+ImVec2(1,1), b: p_max+ImVec2(1,1), col: GetColorU32(idx: ImGuiCol_BorderShadow), rounding, rounding_corners_flags: ImDrawCornerFlags_All, thickness: border_size);
2218 window->DrawList->AddRect(a: p_min, b: p_max, col: GetColorU32(idx: ImGuiCol_Border), rounding, rounding_corners_flags: ImDrawCornerFlags_All, thickness: border_size);
2219 }
2220}
2221
2222void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2223{
2224 ImGuiContext& g = *GImGui;
2225 ImGuiWindow* window = g.CurrentWindow;
2226 const float border_size = g.Style.FrameBorderSize;
2227 if (border_size > 0.0f)
2228 {
2229 window->DrawList->AddRect(a: p_min+ImVec2(1,1), b: p_max+ImVec2(1,1), col: GetColorU32(idx: ImGuiCol_BorderShadow), rounding, rounding_corners_flags: ImDrawCornerFlags_All, thickness: border_size);
2230 window->DrawList->AddRect(a: p_min, b: p_max, col: GetColorU32(idx: ImGuiCol_Border), rounding, rounding_corners_flags: ImDrawCornerFlags_All, thickness: border_size);
2231 }
2232}
2233
2234// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
2235void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2236{
2237 ImGuiContext& g = *GImGui;
2238
2239 const float h = g.FontSize * 1.00f;
2240 float r = h * 0.40f * scale;
2241 ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2242
2243 ImVec2 a, b, c;
2244 switch (dir)
2245 {
2246 case ImGuiDir_Up:
2247 case ImGuiDir_Down:
2248 if (dir == ImGuiDir_Up) r = -r;
2249 a = ImVec2(+0.000f,+0.750f) * r;
2250 b = ImVec2(-0.866f,-0.750f) * r;
2251 c = ImVec2(+0.866f,-0.750f) * r;
2252 break;
2253 case ImGuiDir_Left:
2254 case ImGuiDir_Right:
2255 if (dir == ImGuiDir_Left) r = -r;
2256 a = ImVec2(+0.750f,+0.000f) * r;
2257 b = ImVec2(-0.750f,+0.866f) * r;
2258 c = ImVec2(-0.750f,-0.866f) * r;
2259 break;
2260 case ImGuiDir_None:
2261 case ImGuiDir_COUNT:
2262 IM_ASSERT(0);
2263 break;
2264 }
2265
2266 g.CurrentWindow->DrawList->AddTriangleFilled(a: center + a, b: center + b, c: center + c, col: GetColorU32(idx: ImGuiCol_Text));
2267}
2268
2269void ImGui::RenderBullet(ImVec2 pos)
2270{
2271 ImGuiContext& g = *GImGui;
2272 ImGuiWindow* window = g.CurrentWindow;
2273 window->DrawList->AddCircleFilled(centre: pos, radius: g.FontSize*0.20f, col: GetColorU32(idx: ImGuiCol_Text), num_segments: 8);
2274}
2275
2276void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2277{
2278 ImGuiContext& g = *GImGui;
2279 ImGuiWindow* window = g.CurrentWindow;
2280
2281 float thickness = ImMax(lhs: sz / 5.0f, rhs: 1.0f);
2282 sz -= thickness*0.5f;
2283 pos += ImVec2(thickness*0.25f, thickness*0.25f);
2284
2285 float third = sz / 3.0f;
2286 float bx = pos.x + third;
2287 float by = pos.y + sz - third*0.5f;
2288 window->DrawList->PathLineTo(pos: ImVec2(bx - third, by - third));
2289 window->DrawList->PathLineTo(pos: ImVec2(bx, by));
2290 window->DrawList->PathLineTo(pos: ImVec2(bx + third*2, by - third*2));
2291 window->DrawList->PathStroke(col, closed: false, thickness);
2292}
2293
2294void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2295{
2296 ImGuiContext& g = *GImGui;
2297 if (id != g.NavId)
2298 return;
2299 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2300 return;
2301 ImGuiWindow* window = g.CurrentWindow;
2302 if (window->DC.NavHideHighlightOneFrame)
2303 return;
2304
2305 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2306 ImRect display_rect = bb;
2307 display_rect.ClipWith(r: window->ClipRect);
2308 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2309 {
2310 const float THICKNESS = 2.0f;
2311 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2312 display_rect.Expand(amount: ImVec2(DISTANCE,DISTANCE));
2313 bool fully_visible = window->ClipRect.Contains(r: display_rect);
2314 if (!fully_visible)
2315 window->DrawList->PushClipRect(clip_rect_min: display_rect.Min, clip_rect_max: display_rect.Max);
2316 window->DrawList->AddRect(a: display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), b: display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), col: GetColorU32(idx: ImGuiCol_NavHighlight), rounding, rounding_corners_flags: ImDrawCornerFlags_All, thickness: THICKNESS);
2317 if (!fully_visible)
2318 window->DrawList->PopClipRect();
2319 }
2320 if (flags & ImGuiNavHighlightFlags_TypeThin)
2321 {
2322 window->DrawList->AddRect(a: display_rect.Min, b: display_rect.Max, col: GetColorU32(idx: ImGuiCol_NavHighlight), rounding, rounding_corners_flags: ~0, thickness: 1.0f);
2323 }
2324}
2325
2326//-----------------------------------------------------------------------------
2327// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2328//-----------------------------------------------------------------------------
2329
2330// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
2331ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2332 : DrawListInst(&context->DrawListSharedData)
2333{
2334 Name = ImStrdup(str: name);
2335 ID = ImHash(data: name, data_size: 0);
2336 IDStack.push_back(v: ID);
2337 Flags = 0;
2338 Pos = ImVec2(0.0f, 0.0f);
2339 Size = SizeFull = ImVec2(0.0f, 0.0f);
2340 SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2341 WindowPadding = ImVec2(0.0f, 0.0f);
2342 WindowRounding = 0.0f;
2343 WindowBorderSize = 0.0f;
2344 MoveId = GetID(str: "#MOVE");
2345 ChildId = 0;
2346 Scroll = ImVec2(0.0f, 0.0f);
2347 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2348 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2349 ScrollbarSizes = ImVec2(0.0f, 0.0f);
2350 ScrollbarX = ScrollbarY = false;
2351 Active = WasActive = false;
2352 WriteAccessed = false;
2353 Collapsed = false;
2354 WantCollapseToggle = false;
2355 SkipItems = false;
2356 Appearing = false;
2357 Hidden = false;
2358 HasCloseButton = false;
2359 BeginCount = 0;
2360 BeginOrderWithinParent = -1;
2361 BeginOrderWithinContext = -1;
2362 PopupId = 0;
2363 AutoFitFramesX = AutoFitFramesY = -1;
2364 AutoFitOnlyGrows = false;
2365 AutoFitChildAxises = 0x00;
2366 AutoPosLastDirection = ImGuiDir_None;
2367 HiddenFramesRegular = HiddenFramesForResize = 0;
2368 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2369 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2370
2371 LastFrameActive = -1;
2372 ItemWidthDefault = 0.0f;
2373 FontWindowScale = 1.0f;
2374 SettingsIdx = -1;
2375
2376 DrawList = &DrawListInst;
2377 DrawList->_OwnerName = Name;
2378 ParentWindow = NULL;
2379 RootWindow = NULL;
2380 RootWindowForTitleBarHighlight = NULL;
2381 RootWindowForNav = NULL;
2382
2383 NavLastIds[0] = NavLastIds[1] = 0;
2384 NavRectRel[0] = NavRectRel[1] = ImRect();
2385 NavLastChildNavWindow = NULL;
2386
2387 FocusIdxAllCounter = FocusIdxTabCounter = -1;
2388 FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2389 FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2390}
2391
2392ImGuiWindow::~ImGuiWindow()
2393{
2394 IM_ASSERT(DrawList == &DrawListInst);
2395 IM_DELETE(p: Name);
2396 for (int i = 0; i != ColumnsStorage.Size; i++)
2397 ColumnsStorage[i].~ImGuiColumnsSet();
2398}
2399
2400ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2401{
2402 ImGuiID seed = IDStack.back();
2403 ImGuiID id = ImHash(data: str, data_size: str_end ? (int)(str_end - str) : 0, seed);
2404 ImGui::KeepAliveID(id);
2405 return id;
2406}
2407
2408ImGuiID ImGuiWindow::GetID(const void* ptr)
2409{
2410 ImGuiID seed = IDStack.back();
2411 ImGuiID id = ImHash(data: &ptr, data_size: sizeof(void*), seed);
2412 ImGui::KeepAliveID(id);
2413 return id;
2414}
2415
2416ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2417{
2418 ImGuiID seed = IDStack.back();
2419 return ImHash(data: str, data_size: str_end ? (int)(str_end - str) : 0, seed);
2420}
2421
2422ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2423{
2424 ImGuiID seed = IDStack.back();
2425 return ImHash(data: &ptr, data_size: sizeof(void*), seed);
2426}
2427
2428// This is only used in rare/specific situations to manufacture an ID out of nowhere.
2429ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2430{
2431 ImGuiID seed = IDStack.back();
2432 const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) };
2433 ImGuiID id = ImHash(data: &r_rel, data_size: sizeof(r_rel), seed);
2434 ImGui::KeepAliveID(id);
2435 return id;
2436}
2437
2438static void SetCurrentWindow(ImGuiWindow* window)
2439{
2440 ImGuiContext& g = *GImGui;
2441 g.CurrentWindow = window;
2442 if (window)
2443 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2444}
2445
2446void ImGui::SetNavID(ImGuiID id, int nav_layer)
2447{
2448 ImGuiContext& g = *GImGui;
2449 IM_ASSERT(g.NavWindow);
2450 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2451 g.NavId = id;
2452 g.NavWindow->NavLastIds[nav_layer] = id;
2453}
2454
2455void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2456{
2457 ImGuiContext& g = *GImGui;
2458 SetNavID(id, nav_layer);
2459 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2460 g.NavMousePosDirty = true;
2461 g.NavDisableHighlight = false;
2462 g.NavDisableMouseHover = true;
2463}
2464
2465void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2466{
2467 ImGuiContext& g = *GImGui;
2468 g.ActiveIdIsJustActivated = (g.ActiveId != id);
2469 if (g.ActiveIdIsJustActivated)
2470 {
2471 g.ActiveIdTimer = 0.0f;
2472 g.ActiveIdHasBeenEdited = false;
2473 if (id != 0)
2474 {
2475 g.LastActiveId = id;
2476 g.LastActiveIdTimer = 0.0f;
2477 }
2478 }
2479 g.ActiveId = id;
2480 g.ActiveIdAllowNavDirFlags = 0;
2481 g.ActiveIdAllowOverlap = false;
2482 g.ActiveIdWindow = window;
2483 if (id)
2484 {
2485 g.ActiveIdIsAlive = id;
2486 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2487 }
2488}
2489
2490void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2491{
2492 ImGuiContext& g = *GImGui;
2493 IM_ASSERT(id != 0);
2494
2495 // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2496 const int nav_layer = window->DC.NavLayerCurrent;
2497 if (g.NavWindow != window)
2498 g.NavInitRequest = false;
2499 g.NavId = id;
2500 g.NavWindow = window;
2501 g.NavLayer = nav_layer;
2502 window->NavLastIds[nav_layer] = id;
2503 if (window->DC.LastItemId == id)
2504 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2505
2506 if (g.ActiveIdSource == ImGuiInputSource_Nav)
2507 g.NavDisableMouseHover = true;
2508 else
2509 g.NavDisableHighlight = true;
2510}
2511
2512void ImGui::ClearActiveID()
2513{
2514 SetActiveID(id: 0, NULL);
2515}
2516
2517void ImGui::SetHoveredID(ImGuiID id)
2518{
2519 ImGuiContext& g = *GImGui;
2520 g.HoveredId = id;
2521 g.HoveredIdAllowOverlap = false;
2522 if (id != 0 && g.HoveredIdPreviousFrame != id)
2523 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2524}
2525
2526ImGuiID ImGui::GetHoveredID()
2527{
2528 ImGuiContext& g = *GImGui;
2529 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2530}
2531
2532void ImGui::KeepAliveID(ImGuiID id)
2533{
2534 ImGuiContext& g = *GImGui;
2535 if (g.ActiveId == id)
2536 g.ActiveIdIsAlive = id;
2537 if (g.ActiveIdPreviousFrame == id)
2538 g.ActiveIdPreviousFrameIsAlive = true;
2539}
2540
2541void ImGui::MarkItemEdited(ImGuiID id)
2542{
2543 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2544 // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data.
2545 (void)id; // Avoid unused variable warnings when asserts are compiled out.
2546 ImGuiContext& g = *GImGui;
2547 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2548 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2549 g.ActiveIdHasBeenEdited = true;
2550 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2551}
2552
2553static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2554{
2555 // An active popup disable hovering on other windows (apart from its own children)
2556 // FIXME-OPT: This could be cached/stored within the window.
2557 ImGuiContext& g = *GImGui;
2558 if (g.NavWindow)
2559 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2560 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2561 {
2562 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2563 // NB: The order of those two tests is important because Modal windows are also Popups.
2564 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2565 return false;
2566 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2567 return false;
2568 }
2569
2570 return true;
2571}
2572
2573// Advance cursor given item size for layout.
2574void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2575{
2576 ImGuiContext& g = *GImGui;
2577 ImGuiWindow* window = g.CurrentWindow;
2578 if (window->SkipItems)
2579 return;
2580
2581 // Always align ourselves on pixel boundaries
2582 const float line_height = ImMax(lhs: window->DC.CurrentLineSize.y, rhs: size.y);
2583 const float text_base_offset = ImMax(lhs: window->DC.CurrentLineTextBaseOffset, rhs: text_offset_y);
2584 //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
2585 window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2586 window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
2587 window->DC.CursorMaxPos.x = ImMax(lhs: window->DC.CursorMaxPos.x, rhs: window->DC.CursorPosPrevLine.x);
2588 window->DC.CursorMaxPos.y = ImMax(lhs: window->DC.CursorMaxPos.y, rhs: window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2589 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2590
2591 window->DC.PrevLineSize.y = line_height;
2592 window->DC.PrevLineTextBaseOffset = text_base_offset;
2593 window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2594
2595 // Horizontal layout mode
2596 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2597 SameLine();
2598}
2599
2600void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2601{
2602 ItemSize(size: bb.GetSize(), text_offset_y);
2603}
2604
2605// Declare item bounding box for clipping and interaction.
2606// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2607// declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
2608bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2609{
2610#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
2611 ImGuiTestEngineHook_ItemAdd(bb, id, nav_bb_arg);
2612#endif
2613
2614 ImGuiContext& g = *GImGui;
2615 ImGuiWindow* window = g.CurrentWindow;
2616
2617 if (id != 0)
2618 {
2619 // Navigation processing runs prior to clipping early-out
2620 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2621 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window.
2622 // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2623 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick)
2624 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2625 if (g.NavId == id || g.NavAnyRequest)
2626 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2627 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2628 NavProcessItem(window, nav_bb: nav_bb_arg ? *nav_bb_arg : bb, id);
2629 }
2630
2631 window->DC.LastItemId = id;
2632 window->DC.LastItemRect = bb;
2633 window->DC.LastItemStatusFlags = 0;
2634
2635 // Clipping test
2636 const bool is_clipped = IsClippedEx(bb, id, clip_even_when_logged: false);
2637 if (is_clipped)
2638 return false;
2639 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2640
2641 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2642 if (IsMouseHoveringRect(r_min: bb.Min, r_max: bb.Max))
2643 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2644 return true;
2645}
2646
2647// This is roughly matching the behavior of internal-facing ItemHoverable()
2648// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
2649// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
2650bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2651{
2652 ImGuiContext& g = *GImGui;
2653 ImGuiWindow* window = g.CurrentWindow;
2654 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2655 return IsItemFocused();
2656
2657 // Test for bounding box overlap, as updated as ItemAdd()
2658 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2659 return false;
2660 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
2661
2662 // Test if we are hovering the right window (our window could be behind another window)
2663 // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
2664 // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
2665 //if (g.HoveredWindow != window)
2666 // return false;
2667 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2668 return false;
2669
2670 // Test if another item is active (e.g. being dragged)
2671 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2672 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2673 return false;
2674
2675 // Test if interactions on this window are blocked by an active popup or modal
2676 if (!IsWindowContentHoverable(window, flags))
2677 return false;
2678
2679 // Test if the item is disabled
2680 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2681 return false;
2682
2683 // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
2684 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2685 return false;
2686 return true;
2687}
2688
2689// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
2690bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2691{
2692 ImGuiContext& g = *GImGui;
2693 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2694 return false;
2695
2696 ImGuiWindow* window = g.CurrentWindow;
2697 if (g.HoveredWindow != window)
2698 return false;
2699 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2700 return false;
2701 if (!IsMouseHoveringRect(r_min: bb.Min, r_max: bb.Max))
2702 return false;
2703 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, flags: ImGuiHoveredFlags_None))
2704 return false;
2705 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2706 return false;
2707
2708 SetHoveredID(id);
2709 return true;
2710}
2711
2712bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2713{
2714 ImGuiContext& g = *GImGui;
2715 ImGuiWindow* window = g.CurrentWindow;
2716 if (!bb.Overlaps(r: window->ClipRect))
2717 if (id == 0 || id != g.ActiveId)
2718 if (clip_even_when_logged || !g.LogEnabled)
2719 return true;
2720 return false;
2721}
2722
2723bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2724{
2725 ImGuiContext& g = *GImGui;
2726
2727 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2728 window->FocusIdxAllCounter++;
2729 if (is_tab_stop)
2730 window->FocusIdxTabCounter++;
2731
2732 // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2733 // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2734 if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(key: ImGuiKey_Tab))
2735 window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2736
2737 if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2738 return true;
2739 if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2740 {
2741 g.NavJustTabbedId = id;
2742 return true;
2743 }
2744
2745 return false;
2746}
2747
2748void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2749{
2750 window->FocusIdxAllCounter--;
2751 window->FocusIdxTabCounter--;
2752}
2753
2754ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2755{
2756 ImGuiContext& g = *GImGui;
2757 ImVec2 content_max;
2758 if (size.x < 0.0f || size.y < 0.0f)
2759 content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2760 if (size.x <= 0.0f)
2761 size.x = (size.x == 0.0f) ? default_x : ImMax(lhs: content_max.x - g.CurrentWindow->DC.CursorPos.x, rhs: 4.0f) + size.x;
2762 if (size.y <= 0.0f)
2763 size.y = (size.y == 0.0f) ? default_y : ImMax(lhs: content_max.y - g.CurrentWindow->DC.CursorPos.y, rhs: 4.0f) + size.y;
2764 return size;
2765}
2766
2767float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2768{
2769 if (wrap_pos_x < 0.0f)
2770 return 0.0f;
2771
2772 ImGuiWindow* window = GetCurrentWindowRead();
2773 if (wrap_pos_x == 0.0f)
2774 wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2775 else if (wrap_pos_x > 0.0f)
2776 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2777
2778 return ImMax(lhs: wrap_pos_x - pos.x, rhs: 1.0f);
2779}
2780
2781void* ImGui::MemAlloc(size_t size)
2782{
2783 if (ImGuiContext* ctx = GImGui)
2784 ctx->IO.MetricsActiveAllocations++;
2785 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2786}
2787
2788void ImGui::MemFree(void* ptr)
2789{
2790 if (ptr)
2791 if (ImGuiContext* ctx = GImGui)
2792 ctx->IO.MetricsActiveAllocations--;
2793 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2794}
2795
2796const char* ImGui::GetClipboardText()
2797{
2798 return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2799}
2800
2801void ImGui::SetClipboardText(const char* text)
2802{
2803 if (GImGui->IO.SetClipboardTextFn)
2804 GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2805}
2806
2807const char* ImGui::GetVersion()
2808{
2809 return IMGUI_VERSION;
2810}
2811
2812// Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2813// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
2814ImGuiContext* ImGui::GetCurrentContext()
2815{
2816 return GImGui;
2817}
2818
2819void ImGui::SetCurrentContext(ImGuiContext* ctx)
2820{
2821#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2822 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2823#else
2824 GImGui = ctx;
2825#endif
2826}
2827
2828// Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
2829// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic.
2830bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert)
2831{
2832 bool error = false;
2833 if (strcmp(s1: version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); }
2834 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
2835 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
2836 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
2837 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
2838 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
2839 return !error;
2840}
2841
2842void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
2843{
2844 GImAllocatorAllocFunc = alloc_func;
2845 GImAllocatorFreeFunc = free_func;
2846 GImAllocatorUserData = user_data;
2847}
2848
2849ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2850{
2851 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2852 if (GImGui == NULL)
2853 SetCurrentContext(ctx);
2854 Initialize(context: ctx);
2855 return ctx;
2856}
2857
2858void ImGui::DestroyContext(ImGuiContext* ctx)
2859{
2860 if (ctx == NULL)
2861 ctx = GImGui;
2862 Shutdown(context: ctx);
2863 if (GImGui == ctx)
2864 SetCurrentContext(NULL);
2865 IM_DELETE(p: ctx);
2866}
2867
2868ImGuiIO& ImGui::GetIO()
2869{
2870 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2871 return GImGui->IO;
2872}
2873
2874ImGuiStyle& ImGui::GetStyle()
2875{
2876 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2877 return GImGui->Style;
2878}
2879
2880// Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
2881ImDrawData* ImGui::GetDrawData()
2882{
2883 ImGuiContext& g = *GImGui;
2884 return g.DrawData.Valid ? &g.DrawData : NULL;
2885}
2886
2887double ImGui::GetTime()
2888{
2889 return GImGui->Time;
2890}
2891
2892int ImGui::GetFrameCount()
2893{
2894 return GImGui->FrameCount;
2895}
2896
2897static ImDrawList* GetOverlayDrawList(ImGuiWindow*)
2898{
2899 // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches.
2900 return &GImGui->OverlayDrawList;
2901}
2902
2903ImDrawList* ImGui::GetOverlayDrawList()
2904{
2905 return &GImGui->OverlayDrawList;
2906}
2907
2908ImDrawListSharedData* ImGui::GetDrawListSharedData()
2909{
2910 return &GImGui->DrawListSharedData;
2911}
2912
2913void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
2914{
2915 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
2916 ImGuiContext& g = *GImGui;
2917 FocusWindow(window);
2918 SetActiveID(id: window->MoveId, window);
2919 g.NavDisableHighlight = true;
2920 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
2921 if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
2922 g.MovingWindow = window;
2923}
2924
2925// Handle mouse moving window
2926// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
2927void ImGui::UpdateMouseMovingWindow()
2928{
2929 ImGuiContext& g = *GImGui;
2930 if (g.MovingWindow != NULL)
2931 {
2932 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
2933 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
2934 KeepAliveID(id: g.ActiveId);
2935 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
2936 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
2937 if (g.IO.MouseDown[0] && IsMousePosValid(mouse_pos: &g.IO.MousePos))
2938 {
2939 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
2940 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
2941 {
2942 MarkIniSettingsDirty(window: moving_window);
2943 SetWindowPos(window: moving_window, pos, cond: ImGuiCond_Always);
2944 }
2945 FocusWindow(window: g.MovingWindow);
2946 }
2947 else
2948 {
2949 ClearActiveID();
2950 g.MovingWindow = NULL;
2951 }
2952 }
2953 else
2954 {
2955 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
2956 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
2957 {
2958 KeepAliveID(id: g.ActiveId);
2959 if (!g.IO.MouseDown[0])
2960 ClearActiveID();
2961 }
2962 }
2963}
2964
2965static bool IsWindowActiveAndVisible(ImGuiWindow* window)
2966{
2967 return (window->Active) && (!window->Hidden);
2968}
2969
2970static void ImGui::UpdateMouseInputs()
2971{
2972 ImGuiContext& g = *GImGui;
2973
2974 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
2975 if (IsMousePosValid(mouse_pos: &g.IO.MousePos))
2976 g.IO.MousePos = g.LastValidMousePos = ImFloor(v: g.IO.MousePos);
2977
2978 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
2979 if (IsMousePosValid(mouse_pos: &g.IO.MousePos) && IsMousePosValid(mouse_pos: &g.IO.MousePosPrev))
2980 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2981 else
2982 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2983 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
2984 g.NavDisableMouseHover = false;
2985
2986 g.IO.MousePosPrev = g.IO.MousePos;
2987 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2988 {
2989 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2990 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2991 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2992 g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2993 g.IO.MouseDoubleClicked[i] = false;
2994 if (g.IO.MouseClicked[i])
2995 {
2996 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
2997 {
2998 ImVec2 delta_from_click_pos = IsMousePosValid(mouse_pos: &g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
2999 if (ImLengthSqr(lhs: delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3000 g.IO.MouseDoubleClicked[i] = true;
3001 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
3002 }
3003 else
3004 {
3005 g.IO.MouseClickedTime[i] = g.Time;
3006 }
3007 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3008 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3009 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3010 }
3011 else if (g.IO.MouseDown[i])
3012 {
3013 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3014 ImVec2 delta_from_click_pos = IsMousePosValid(mouse_pos: &g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3015 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(lhs: g.IO.MouseDragMaxDistanceSqr[i], rhs: ImLengthSqr(lhs: delta_from_click_pos));
3016 g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(lhs: g.IO.MouseDragMaxDistanceAbs[i].x, rhs: delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
3017 g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(lhs: g.IO.MouseDragMaxDistanceAbs[i].y, rhs: delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
3018 }
3019 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3020 g.NavDisableMouseHover = false;
3021 }
3022}
3023
3024void ImGui::UpdateMouseWheel()
3025{
3026 ImGuiContext& g = *GImGui;
3027 if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
3028 return;
3029 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3030 return;
3031
3032 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set).
3033 ImGuiWindow* window = g.HoveredWindow;
3034 ImGuiWindow* scroll_window = window;
3035 while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs) && scroll_window->ParentWindow)
3036 scroll_window = scroll_window->ParentWindow;
3037 const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs);
3038
3039 if (g.IO.MouseWheel != 0.0f)
3040 {
3041 if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3042 {
3043 // Zoom / Scale window
3044 const float new_font_scale = ImClamp(v: window->FontWindowScale + g.IO.MouseWheel * 0.10f, mn: 0.50f, mx: 2.50f);
3045 const float scale = new_font_scale / window->FontWindowScale;
3046 window->FontWindowScale = new_font_scale;
3047
3048 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3049 window->Pos += offset;
3050 window->Size *= scale;
3051 window->SizeFull *= scale;
3052 }
3053 else if (!g.IO.KeyCtrl && scroll_allowed)
3054 {
3055 // Mouse wheel vertical scrolling
3056 float scroll_amount = 5 * scroll_window->CalcFontSize();
3057 scroll_amount = (float)(int)ImMin(lhs: scroll_amount, rhs: (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3058 SetWindowScrollY(window: scroll_window, new_scroll_y: scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3059 }
3060 }
3061 if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3062 {
3063 // Mouse wheel horizontal scrolling (for hardware that supports it)
3064 float scroll_amount = scroll_window->CalcFontSize();
3065 SetWindowScrollX(window: scroll_window, new_scroll_x: scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3066 }
3067}
3068
3069// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
3070void ImGui::UpdateHoveredWindowAndCaptureFlags()
3071{
3072 ImGuiContext& g = *GImGui;
3073
3074 // Find the window hovered by mouse:
3075 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3076 // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
3077 // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
3078 FindHoveredWindow();
3079
3080 // Modal windows prevents cursor from hovering behind them.
3081 ImGuiWindow* modal_window = GetFrontMostPopupModal();
3082 if (modal_window)
3083 if (g.HoveredRootWindow && !IsWindowChildOf(window: g.HoveredRootWindow, potential_parent: modal_window))
3084 g.HoveredRootWindow = g.HoveredWindow = NULL;
3085
3086 // Disabled mouse?
3087 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3088 g.HoveredWindow = g.HoveredRootWindow = NULL;
3089
3090 // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
3091 int mouse_earliest_button_down = -1;
3092 bool mouse_any_down = false;
3093 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3094 {
3095 if (g.IO.MouseClicked[i])
3096 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3097 mouse_any_down |= g.IO.MouseDown[i];
3098 if (g.IO.MouseDown[i])
3099 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3100 mouse_earliest_button_down = i;
3101 }
3102 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3103
3104 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3105 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3106 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3107 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3108 g.HoveredWindow = g.HoveredRootWindow = NULL;
3109
3110 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3111 if (g.WantCaptureMouseNextFrame != -1)
3112 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3113 else
3114 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3115
3116 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3117 if (g.WantCaptureKeyboardNextFrame != -1)
3118 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3119 else
3120 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3121 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3122 g.IO.WantCaptureKeyboard = true;
3123
3124 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3125 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3126}
3127
3128void ImGui::NewFrame()
3129{
3130 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3131 ImGuiContext& g = *GImGui;
3132
3133#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
3134 ImGuiTestEngineHook_PreNewFrame();
3135#endif
3136
3137 // Check user data
3138 // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)
3139 IM_ASSERT(g.Initialized);
3140 IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3141 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value");
3142 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3143 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3144 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting");
3145 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)");
3146 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3147 for (int n = 0; n < ImGuiKey_COUNT; n++)
3148 IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");
3149
3150 // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
3151 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3152 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3153
3154 // Perform simple check: the beta io.ConfigResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3155 if (g.IO.ConfigResizeWindowsFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3156 g.IO.ConfigResizeWindowsFromEdges = false;
3157
3158 // Load settings on first frame (if not explicitly loaded manually before)
3159 if (!g.SettingsLoaded)
3160 {
3161 IM_ASSERT(g.SettingsWindows.empty());
3162 if (g.IO.IniFilename)
3163 LoadIniSettingsFromDisk(ini_filename: g.IO.IniFilename);
3164 g.SettingsLoaded = true;
3165 }
3166
3167 // Save settings (with a delay after the last modification, so we don't spam disk too much)
3168 if (g.SettingsDirtyTimer > 0.0f)
3169 {
3170 g.SettingsDirtyTimer -= g.IO.DeltaTime;
3171 if (g.SettingsDirtyTimer <= 0.0f)
3172 {
3173 if (g.IO.IniFilename != NULL)
3174 SaveIniSettingsToDisk(ini_filename: g.IO.IniFilename);
3175 else
3176 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3177 g.SettingsDirtyTimer = 0.0f;
3178 }
3179 }
3180
3181 g.Time += g.IO.DeltaTime;
3182 g.FrameScopeActive = true;
3183 g.FrameCount += 1;
3184 g.TooltipOverrideCount = 0;
3185 g.WindowsActiveCount = 0;
3186
3187 // Setup current font and draw list
3188 g.IO.Fonts->Locked = true;
3189 SetCurrentFont(GetDefaultFont());
3190 IM_ASSERT(g.Font->IsLoaded());
3191 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3192 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3193
3194 g.OverlayDrawList.Clear();
3195 g.OverlayDrawList.PushTextureID(texture_id: g.IO.Fonts->TexID);
3196 g.OverlayDrawList.PushClipRectFullScreen();
3197 g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3198
3199 // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3200 g.DrawData.Clear();
3201
3202 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3203 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3204 KeepAliveID(id: g.DragDropPayload.SourceId);
3205
3206 // Clear reference to active widget if the widget isn't alive anymore
3207 if (!g.HoveredIdPreviousFrame)
3208 g.HoveredIdTimer = 0.0f;
3209 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3210 g.HoveredIdNotActiveTimer = 0.0f;
3211 if (g.HoveredId)
3212 g.HoveredIdTimer += g.IO.DeltaTime;
3213 if (g.HoveredId && g.ActiveId != g.HoveredId)
3214 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3215 g.HoveredIdPreviousFrame = g.HoveredId;
3216 g.HoveredId = 0;
3217 g.HoveredIdAllowOverlap = false;
3218 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3219 ClearActiveID();
3220 if (g.ActiveId)
3221 g.ActiveIdTimer += g.IO.DeltaTime;
3222 g.LastActiveIdTimer += g.IO.DeltaTime;
3223 g.ActiveIdPreviousFrame = g.ActiveId;
3224 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3225 g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3226 g.ActiveIdIsAlive = 0;
3227 g.ActiveIdPreviousFrameIsAlive = false;
3228 g.ActiveIdIsJustActivated = false;
3229 if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3230 g.ScalarAsInputTextId = 0;
3231
3232 // Drag and drop
3233 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3234 g.DragDropAcceptIdCurr = 0;
3235 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3236 g.DragDropWithinSourceOrTarget = false;
3237
3238 // Update keyboard input state
3239 memcpy(dest: g.IO.KeysDownDurationPrev, src: g.IO.KeysDownDuration, n: sizeof(g.IO.KeysDownDuration));
3240 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3241 g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
3242
3243 // Update gamepad/keyboard directional navigation
3244 NavUpdate();
3245
3246 // Update mouse input state
3247 UpdateMouseInputs();
3248
3249 // Calculate frame-rate for the user, as a purely luxurious feature
3250 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3251 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3252 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3253 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3254
3255 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3256 UpdateMouseMovingWindow();
3257 UpdateHoveredWindowAndCaptureFlags();
3258
3259 // Background darkening/whitening
3260 if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3261 g.DimBgRatio = ImMin(lhs: g.DimBgRatio + g.IO.DeltaTime * 6.0f, rhs: 1.0f);
3262 else
3263 g.DimBgRatio = ImMax(lhs: g.DimBgRatio - g.IO.DeltaTime * 10.0f, rhs: 0.0f);
3264
3265 g.MouseCursor = ImGuiMouseCursor_Arrow;
3266 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3267 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3268
3269 // Mouse wheel scrolling, scale
3270 UpdateMouseWheel();
3271
3272 // Pressing TAB activate widget focus
3273 if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(key: ImGuiKey_Tab, repeat: false))
3274 {
3275 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3276 g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3277 else
3278 g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3279 }
3280 g.NavIdTabCounter = INT_MAX;
3281
3282 // Mark all windows as not visible
3283 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3284 for (int i = 0; i != g.Windows.Size; i++)
3285 {
3286 ImGuiWindow* window = g.Windows[i];
3287 window->WasActive = window->Active;
3288 window->Active = false;
3289 window->WriteAccessed = false;
3290 }
3291
3292 // Closing the focused window restore focus to the first active root window in descending z-order
3293 if (g.NavWindow && !g.NavWindow->WasActive)
3294 FocusPreviousWindowIgnoringOne(NULL);
3295
3296 // No window should be open at the beginning of the frame.
3297 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3298 g.CurrentWindowStack.resize(new_size: 0);
3299 g.CurrentPopupStack.resize(new_size: 0);
3300 ClosePopupsOverWindow(ref_window: g.NavWindow);
3301
3302 // Create implicit window - we will only render it if the user has added something to it.
3303 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3304 SetNextWindowSize(size: ImVec2(400,400), cond: ImGuiCond_FirstUseEver);
3305 Begin(name: "Debug##Default");
3306
3307#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
3308 ImGuiTestEngineHook_PostNewFrame();
3309#endif
3310}
3311
3312void ImGui::Initialize(ImGuiContext* context)
3313{
3314 ImGuiContext& g = *context;
3315 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3316
3317 // Add .ini handle for ImGuiWindow type
3318 ImGuiSettingsHandler ini_handler;
3319 ini_handler.TypeName = "Window";
3320 ini_handler.TypeHash = ImHash(data: "Window", data_size: 0, seed: 0);
3321 ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3322 ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3323 ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3324 g.SettingsHandlers.push_front(v: ini_handler);
3325
3326 g.Initialized = true;
3327}
3328
3329// This function is merely here to free heap allocations.
3330void ImGui::Shutdown(ImGuiContext* context)
3331{
3332 // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
3333 ImGuiContext& g = *context;
3334 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3335 {
3336 g.IO.Fonts->Locked = false;
3337 IM_DELETE(p: g.IO.Fonts);
3338 }
3339 g.IO.Fonts = NULL;
3340
3341 // Cleanup of other data are conditional on actually having initialized ImGui.
3342 if (!g.Initialized)
3343 return;
3344
3345 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3346 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3347 {
3348 ImGuiContext* backup_context = GImGui;
3349 SetCurrentContext(context);
3350 SaveIniSettingsToDisk(ini_filename: g.IO.IniFilename);
3351 SetCurrentContext(backup_context);
3352 }
3353
3354 // Clear everything else
3355 for (int i = 0; i < g.Windows.Size; i++)
3356 IM_DELETE(p: g.Windows[i]);
3357 g.Windows.clear();
3358 g.WindowsFocusOrder.clear();
3359 g.WindowsSortBuffer.clear();
3360 g.CurrentWindow = NULL;
3361 g.CurrentWindowStack.clear();
3362 g.WindowsById.Clear();
3363 g.NavWindow = NULL;
3364 g.HoveredWindow = NULL;
3365 g.HoveredRootWindow = NULL;
3366 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3367 g.MovingWindow = NULL;
3368 g.ColorModifiers.clear();
3369 g.StyleModifiers.clear();
3370 g.FontStack.clear();
3371 g.OpenPopupStack.clear();
3372 g.CurrentPopupStack.clear();
3373 g.DrawDataBuilder.ClearFreeMemory();
3374 g.OverlayDrawList.ClearFreeMemory();
3375 g.PrivateClipboard.clear();
3376 g.InputTextState.TextW.clear();
3377 g.InputTextState.InitialText.clear();
3378 g.InputTextState.TempBuffer.clear();
3379
3380 for (int i = 0; i < g.SettingsWindows.Size; i++)
3381 IM_DELETE(p: g.SettingsWindows[i].Name);
3382 g.SettingsWindows.clear();
3383 g.SettingsHandlers.clear();
3384
3385 if (g.LogFile && g.LogFile != stdout)
3386 {
3387 fclose(stream: g.LogFile);
3388 g.LogFile = NULL;
3389 }
3390 g.LogClipboard.clear();
3391
3392 g.Initialized = false;
3393}
3394
3395// FIXME: Add a more explicit sort order in the window structure.
3396static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3397{
3398 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3399 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3400 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3401 return d;
3402 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3403 return d;
3404 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3405}
3406
3407static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3408{
3409 out_sorted_windows->push_back(v: window);
3410 if (window->Active)
3411 {
3412 int count = window->DC.ChildWindows.Size;
3413 if (count > 1)
3414 ImQsort(base: window->DC.ChildWindows.begin(), nmemb: (size_t)count, size: sizeof(ImGuiWindow*), compar: ChildWindowComparer);
3415 for (int i = 0; i < count; i++)
3416 {
3417 ImGuiWindow* child = window->DC.ChildWindows[i];
3418 if (child->Active)
3419 AddWindowToSortBuffer(out_sorted_windows, window: child);
3420 }
3421 }
3422}
3423
3424static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3425{
3426 if (draw_list->CmdBuffer.empty())
3427 return;
3428
3429 // Remove trailing command if unused
3430 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3431 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3432 {
3433 draw_list->CmdBuffer.pop_back();
3434 if (draw_list->CmdBuffer.empty())
3435 return;
3436 }
3437
3438 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
3439 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3440 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3441 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3442
3443 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3444 // If this assert triggers because you are drawing lots of stuff manually:
3445 // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents.
3446 // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes.
3447 // You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing:
3448 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3449 // Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
3450 // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
3451 if (sizeof(ImDrawIdx) == 2)
3452 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3453
3454 out_list->push_back(v: draw_list);
3455}
3456
3457static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3458{
3459 ImGuiContext& g = *GImGui;
3460 g.IO.MetricsRenderWindows++;
3461 AddDrawListToDrawData(out_list: out_render_list, draw_list: window->DrawList);
3462 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3463 {
3464 ImGuiWindow* child = window->DC.ChildWindows[i];
3465 if (IsWindowActiveAndVisible(window: child)) // clipped children may have been marked not active
3466 AddWindowToDrawData(out_render_list, window: child);
3467 }
3468}
3469
3470static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3471{
3472 ImGuiContext& g = *GImGui;
3473 if (window->Flags & ImGuiWindowFlags_Tooltip)
3474 AddWindowToDrawData(out_render_list: &g.DrawDataBuilder.Layers[1], window);
3475 else
3476 AddWindowToDrawData(out_render_list: &g.DrawDataBuilder.Layers[0], window);
3477}
3478
3479void ImDrawDataBuilder::FlattenIntoSingleLayer()
3480{
3481 int n = Layers[0].Size;
3482 int size = n;
3483 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3484 size += Layers[i].Size;
3485 Layers[0].resize(new_size: size);
3486 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3487 {
3488 ImVector<ImDrawList*>& layer = Layers[layer_n];
3489 if (layer.empty())
3490 continue;
3491 memcpy(dest: &Layers[0][n], src: &layer[0], n: layer.Size * sizeof(ImDrawList*));
3492 n += layer.Size;
3493 layer.resize(new_size: 0);
3494 }
3495}
3496
3497static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3498{
3499 ImGuiIO& io = ImGui::GetIO();
3500 draw_data->Valid = true;
3501 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3502 draw_data->CmdListsCount = draw_lists->Size;
3503 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3504 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3505 draw_data->DisplaySize = io.DisplaySize;
3506 for (int n = 0; n < draw_lists->Size; n++)
3507 {
3508 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3509 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3510 }
3511}
3512
3513// When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
3514void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3515{
3516 ImGuiWindow* window = GetCurrentWindow();
3517 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3518 window->ClipRect = window->DrawList->_ClipRectStack.back();
3519}
3520
3521void ImGui::PopClipRect()
3522{
3523 ImGuiWindow* window = GetCurrentWindow();
3524 window->DrawList->PopClipRect();
3525 window->ClipRect = window->DrawList->_ClipRectStack.back();
3526}
3527
3528// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
3529void ImGui::EndFrame()
3530{
3531 ImGuiContext& g = *GImGui;
3532 IM_ASSERT(g.Initialized);
3533 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
3534 return;
3535 IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()");
3536
3537 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3538 if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(lhs: g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)
3539 {
3540 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3541 g.PlatformImeLastPos = g.PlatformImePos;
3542 }
3543
3544 // Hide implicit "Debug" window if it hasn't been used
3545 IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name?
3546 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3547 g.CurrentWindow->Active = false;
3548 End();
3549
3550 // Show CTRL+TAB list
3551 if (g.NavWindowingTarget)
3552 NavUpdateWindowingList();
3553
3554 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3555 if (g.DragDropActive)
3556 {
3557 bool is_delivered = g.DragDropPayload.Delivery;
3558 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(button: g.DragDropMouseButton));
3559 if (is_delivered || is_elapsed)
3560 ClearDragDrop();
3561 }
3562
3563 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3564 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3565 {
3566 g.DragDropWithinSourceOrTarget = true;
3567 SetTooltip("...");
3568 g.DragDropWithinSourceOrTarget = false;
3569 }
3570
3571 // Initiate moving window
3572 if (g.ActiveId == 0 && g.HoveredId == 0)
3573 {
3574 if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
3575 {
3576 // Click to focus window and start moving (after we're done with all our widgets)
3577 if (g.IO.MouseClicked[0])
3578 {
3579 if (g.HoveredRootWindow != NULL)
3580 StartMouseMovingWindow(window: g.HoveredWindow);
3581 else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3582 FocusWindow(NULL); // Clicking on void disable focus
3583 }
3584
3585 // With right mouse button we close popups without changing focus
3586 // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3587 if (g.IO.MouseClicked[1])
3588 {
3589 // Find the top-most window between HoveredWindow and the front most Modal Window.
3590 // This is where we can trim the popup stack.
3591 ImGuiWindow* modal = GetFrontMostPopupModal();
3592 bool hovered_window_above_modal = false;
3593 if (modal == NULL)
3594 hovered_window_above_modal = true;
3595 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3596 {
3597 ImGuiWindow* window = g.Windows[i];
3598 if (window == modal)
3599 break;
3600 if (window == g.HoveredWindow)
3601 hovered_window_above_modal = true;
3602 }
3603 ClosePopupsOverWindow(ref_window: hovered_window_above_modal ? g.HoveredWindow : modal);
3604 }
3605 }
3606 }
3607
3608 // Sort the window list so that all child windows are after their parent
3609 // We cannot do that on FocusWindow() because childs may not exist yet
3610 g.WindowsSortBuffer.resize(new_size: 0);
3611 g.WindowsSortBuffer.reserve(new_capacity: g.Windows.Size);
3612 for (int i = 0; i != g.Windows.Size; i++)
3613 {
3614 ImGuiWindow* window = g.Windows[i];
3615 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
3616 continue;
3617 AddWindowToSortBuffer(out_sorted_windows: &g.WindowsSortBuffer, window);
3618 }
3619
3620 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
3621 g.Windows.swap(rhs&: g.WindowsSortBuffer);
3622 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3623
3624 // Unlock font atlas
3625 g.IO.Fonts->Locked = false;
3626
3627 // Clear Input data for next frame
3628 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3629 memset(s: g.IO.InputCharacters, c: 0, n: sizeof(g.IO.InputCharacters));
3630 memset(s: g.IO.NavInputs, c: 0, n: sizeof(g.IO.NavInputs));
3631
3632 g.FrameScopeActive = false;
3633 g.FrameCountEnded = g.FrameCount;
3634}
3635
3636void ImGui::Render()
3637{
3638 ImGuiContext& g = *GImGui;
3639 IM_ASSERT(g.Initialized);
3640
3641 if (g.FrameCountEnded != g.FrameCount)
3642 EndFrame();
3643 g.FrameCountRendered = g.FrameCount;
3644
3645 // Gather ImDrawList to render (for each active window)
3646 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3647 g.DrawDataBuilder.Clear();
3648 ImGuiWindow* windows_to_render_front_most[2];
3649 windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3650 windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3651 for (int n = 0; n != g.Windows.Size; n++)
3652 {
3653 ImGuiWindow* window = g.Windows[n];
3654 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3655 AddWindowToDrawDataSelectLayer(window);
3656 }
3657 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3658 if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(window: windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3659 AddWindowToDrawDataSelectLayer(window: windows_to_render_front_most[n]);
3660 g.DrawDataBuilder.FlattenIntoSingleLayer();
3661
3662 // Draw software mouse cursor if requested
3663 if (g.IO.MouseDrawCursor)
3664 RenderMouseCursor(draw_list: &g.OverlayDrawList, pos: g.IO.MousePos, scale: g.Style.MouseCursorScale, mouse_cursor: g.MouseCursor);
3665
3666 if (!g.OverlayDrawList.VtxBuffer.empty())
3667 AddDrawListToDrawData(out_list: &g.DrawDataBuilder.Layers[0], draw_list: &g.OverlayDrawList);
3668
3669 // Setup ImDrawData structure for end-user
3670 SetupDrawData(draw_lists: &g.DrawDataBuilder.Layers[0], draw_data: &g.DrawData);
3671 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3672 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3673
3674 // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
3675#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3676 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3677 g.IO.RenderDrawListsFn(&g.DrawData);
3678#endif
3679}
3680
3681// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3682// CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
3683ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3684{
3685 ImGuiContext& g = *GImGui;
3686
3687 const char* text_display_end;
3688 if (hide_text_after_double_hash)
3689 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
3690 else
3691 text_display_end = text_end;
3692
3693 ImFont* font = g.Font;
3694 const float font_size = g.FontSize;
3695 if (text == text_display_end)
3696 return ImVec2(0.0f, font_size);
3697 ImVec2 text_size = font->CalcTextSizeA(size: font_size, FLT_MAX, wrap_width, text_begin: text, text_end: text_display_end, NULL);
3698
3699 // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3700 const float font_scale = font_size / font->FontSize;
3701 const float character_spacing_x = 1.0f * font_scale;
3702 if (text_size.x > 0.0f)
3703 text_size.x -= character_spacing_x;
3704 text_size.x = (float)(int)(text_size.x + 0.95f);
3705
3706 return text_size;
3707}
3708
3709// Helper to calculate coarse clipping of large list of evenly sized items.
3710// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3711// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
3712void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3713{
3714 ImGuiContext& g = *GImGui;
3715 ImGuiWindow* window = g.CurrentWindow;
3716 if (g.LogEnabled)
3717 {
3718 // If logging is active, do not perform any clipping
3719 *out_items_display_start = 0;
3720 *out_items_display_end = items_count;
3721 return;
3722 }
3723 if (window->SkipItems)
3724 {
3725 *out_items_display_start = *out_items_display_end = 0;
3726 return;
3727 }
3728
3729 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3730 ImRect unclipped_rect = window->ClipRect;
3731 if (g.NavMoveRequest)
3732 unclipped_rect.Add(r: g.NavScoringRectScreen);
3733
3734 const ImVec2 pos = window->DC.CursorPos;
3735 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3736 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3737
3738 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
3739 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3740 start--;
3741 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3742 end++;
3743
3744 start = ImClamp(v: start, mn: 0, mx: items_count);
3745 end = ImClamp(v: end + 1, mn: start, mx: items_count);
3746 *out_items_display_start = start;
3747 *out_items_display_end = end;
3748}
3749
3750// Find window given position, search front-to-back
3751// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3752// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3753// called, aka before the next Begin(). Moving window isn't affected.
3754static void FindHoveredWindow()
3755{
3756 ImGuiContext& g = *GImGui;
3757
3758 ImGuiWindow* hovered_window = NULL;
3759 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
3760 hovered_window = g.MovingWindow;
3761
3762 ImVec2 padding_regular = g.Style.TouchExtraPadding;
3763 ImVec2 padding_for_resize_from_edges = g.IO.ConfigResizeWindowsFromEdges ? ImMax(lhs: g.Style.TouchExtraPadding, rhs: ImVec2(RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
3764 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--)
3765 {
3766 ImGuiWindow* window = g.Windows[i];
3767 if (!window->Active || window->Hidden)
3768 continue;
3769 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
3770 continue;
3771
3772 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3773 ImRect bb(window->OuterRectClipped);
3774 if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize))
3775 bb.Expand(amount: padding_regular);
3776 else
3777 bb.Expand(amount: padding_for_resize_from_edges);
3778 if (!bb.Contains(p: g.IO.MousePos))
3779 continue;
3780
3781 // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
3782 if (hovered_window == NULL)
3783 hovered_window = window;
3784 if (hovered_window)
3785 break;
3786 }
3787
3788 g.HoveredWindow = hovered_window;
3789 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3790
3791}
3792
3793// Test if mouse cursor is hovering given rectangle
3794// NB- Rectangle is clipped by our current clip setting
3795// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
3796bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3797{
3798 ImGuiContext& g = *GImGui;
3799
3800 // Clip
3801 ImRect rect_clipped(r_min, r_max);
3802 if (clip)
3803 rect_clipped.ClipWith(r: g.CurrentWindow->ClipRect);
3804
3805 // Expand for touch input
3806 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3807 if (!rect_for_touch.Contains(p: g.IO.MousePos))
3808 return false;
3809 return true;
3810}
3811
3812int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3813{
3814 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3815 return GImGui->IO.KeyMap[imgui_key];
3816}
3817
3818// Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
3819bool ImGui::IsKeyDown(int user_key_index)
3820{
3821 if (user_key_index < 0) return false;
3822 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3823 return GImGui->IO.KeysDown[user_key_index];
3824}
3825
3826int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3827{
3828 if (t == 0.0f)
3829 return 1;
3830 if (t <= repeat_delay || repeat_rate <= 0.0f)
3831 return 0;
3832 const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3833 return (count > 0) ? count : 0;
3834}
3835
3836int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3837{
3838 ImGuiContext& g = *GImGui;
3839 if (key_index < 0) return false;
3840 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3841 const float t = g.IO.KeysDownDuration[key_index];
3842 return CalcTypematicPressedRepeatAmount(t, t_prev: t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3843}
3844
3845bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3846{
3847 ImGuiContext& g = *GImGui;
3848 if (user_key_index < 0) return false;
3849 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3850 const float t = g.IO.KeysDownDuration[user_key_index];
3851 if (t == 0.0f)
3852 return true;
3853 if (repeat && t > g.IO.KeyRepeatDelay)
3854 return GetKeyPressedAmount(key_index: user_key_index, repeat_delay: g.IO.KeyRepeatDelay, repeat_rate: g.IO.KeyRepeatRate) > 0;
3855 return false;
3856}
3857
3858bool ImGui::IsKeyReleased(int user_key_index)
3859{
3860 ImGuiContext& g = *GImGui;
3861 if (user_key_index < 0) return false;
3862 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3863 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
3864}
3865
3866bool ImGui::IsMouseDown(int button)
3867{
3868 ImGuiContext& g = *GImGui;
3869 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3870 return g.IO.MouseDown[button];
3871}
3872
3873bool ImGui::IsAnyMouseDown()
3874{
3875 ImGuiContext& g = *GImGui;
3876 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
3877 if (g.IO.MouseDown[n])
3878 return true;
3879 return false;
3880}
3881
3882bool ImGui::IsMouseClicked(int button, bool repeat)
3883{
3884 ImGuiContext& g = *GImGui;
3885 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3886 const float t = g.IO.MouseDownDuration[button];
3887 if (t == 0.0f)
3888 return true;
3889
3890 if (repeat && t > g.IO.KeyRepeatDelay)
3891 {
3892 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3893 if ((ImFmod(x: t - delay, y: rate) > rate*0.5f) != (ImFmod(x: t - delay - g.IO.DeltaTime, y: rate) > rate*0.5f))
3894 return true;
3895 }
3896
3897 return false;
3898}
3899
3900bool ImGui::IsMouseReleased(int button)
3901{
3902 ImGuiContext& g = *GImGui;
3903 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3904 return g.IO.MouseReleased[button];
3905}
3906
3907bool ImGui::IsMouseDoubleClicked(int button)
3908{
3909 ImGuiContext& g = *GImGui;
3910 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3911 return g.IO.MouseDoubleClicked[button];
3912}
3913
3914bool ImGui::IsMouseDragging(int button, float lock_threshold)
3915{
3916 ImGuiContext& g = *GImGui;
3917 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3918 if (!g.IO.MouseDown[button])
3919 return false;
3920 if (lock_threshold < 0.0f)
3921 lock_threshold = g.IO.MouseDragThreshold;
3922 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3923}
3924
3925ImVec2 ImGui::GetMousePos()
3926{
3927 return GImGui->IO.MousePos;
3928}
3929
3930// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
3931ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3932{
3933 ImGuiContext& g = *GImGui;
3934 if (g.CurrentPopupStack.Size > 0)
3935 return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos;
3936 return g.IO.MousePos;
3937}
3938
3939// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
3940bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
3941{
3942 if (mouse_pos == NULL)
3943 mouse_pos = &GImGui->IO.MousePos;
3944 const float MOUSE_INVALID = -256000.0f;
3945 return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
3946}
3947
3948// NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
3949ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3950{
3951 ImGuiContext& g = *GImGui;
3952 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3953 if (lock_threshold < 0.0f)
3954 lock_threshold = g.IO.MouseDragThreshold;
3955 if (g.IO.MouseDown[button])
3956 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3957 return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment).
3958 return ImVec2(0.0f, 0.0f);
3959}
3960
3961void ImGui::ResetMouseDragDelta(int button)
3962{
3963 ImGuiContext& g = *GImGui;
3964 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3965 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3966 g.IO.MouseClickedPos[button] = g.IO.MousePos;
3967}
3968
3969ImGuiMouseCursor ImGui::GetMouseCursor()
3970{
3971 return GImGui->MouseCursor;
3972}
3973
3974void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3975{
3976 GImGui->MouseCursor = cursor_type;
3977}
3978
3979void ImGui::CaptureKeyboardFromApp(bool capture)
3980{
3981 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
3982}
3983
3984void ImGui::CaptureMouseFromApp(bool capture)
3985{
3986 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
3987}
3988
3989bool ImGui::IsItemActive()
3990{
3991 ImGuiContext& g = *GImGui;
3992 if (g.ActiveId)
3993 {
3994 ImGuiWindow* window = g.CurrentWindow;
3995 return g.ActiveId == window->DC.LastItemId;
3996 }
3997 return false;
3998}
3999
4000bool ImGui::IsItemDeactivated()
4001{
4002 ImGuiContext& g = *GImGui;
4003 ImGuiWindow* window = g.CurrentWindow;
4004 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4005}
4006
4007bool ImGui::IsItemDeactivatedAfterEdit()
4008{
4009 ImGuiContext& g = *GImGui;
4010 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
4011}
4012
4013bool ImGui::IsItemFocused()
4014{
4015 ImGuiContext& g = *GImGui;
4016 return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
4017}
4018
4019bool ImGui::IsItemClicked(int mouse_button)
4020{
4021 return IsMouseClicked(button: mouse_button) && IsItemHovered(flags: ImGuiHoveredFlags_None);
4022}
4023
4024bool ImGui::IsAnyItemHovered()
4025{
4026 ImGuiContext& g = *GImGui;
4027 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4028}
4029
4030bool ImGui::IsAnyItemActive()
4031{
4032 ImGuiContext& g = *GImGui;
4033 return g.ActiveId != 0;
4034}
4035
4036bool ImGui::IsAnyItemFocused()
4037{
4038 ImGuiContext& g = *GImGui;
4039 return g.NavId != 0 && !g.NavDisableHighlight;
4040}
4041
4042bool ImGui::IsItemVisible()
4043{
4044 ImGuiWindow* window = GetCurrentWindowRead();
4045 return window->ClipRect.Overlaps(r: window->DC.LastItemRect);
4046}
4047
4048bool ImGui::IsItemEdited()
4049{
4050 ImGuiWindow* window = GetCurrentWindowRead();
4051 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4052}
4053
4054// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
4055void ImGui::SetItemAllowOverlap()
4056{
4057 ImGuiContext& g = *GImGui;
4058 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4059 g.HoveredIdAllowOverlap = true;
4060 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4061 g.ActiveIdAllowOverlap = true;
4062}
4063
4064ImVec2 ImGui::GetItemRectMin()
4065{
4066 ImGuiWindow* window = GetCurrentWindowRead();
4067 return window->DC.LastItemRect.Min;
4068}
4069
4070ImVec2 ImGui::GetItemRectMax()
4071{
4072 ImGuiWindow* window = GetCurrentWindowRead();
4073 return window->DC.LastItemRect.Max;
4074}
4075
4076ImVec2 ImGui::GetItemRectSize()
4077{
4078 ImGuiWindow* window = GetCurrentWindowRead();
4079 return window->DC.LastItemRect.GetSize();
4080}
4081
4082static ImRect GetViewportRect()
4083{
4084 ImGuiContext& g = *GImGui;
4085 if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4086 return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4087 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4088}
4089
4090static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4091{
4092 ImGuiContext& g = *GImGui;
4093 ImGuiWindow* parent_window = g.CurrentWindow;
4094
4095 flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4096 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4097
4098 // Size
4099 const ImVec2 content_avail = GetContentRegionAvail();
4100 ImVec2 size = ImFloor(v: size_arg);
4101 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4102 if (size.x <= 0.0f)
4103 size.x = ImMax(lhs: content_avail.x + size.x, rhs: 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4104 if (size.y <= 0.0f)
4105 size.y = ImMax(lhs: content_avail.y + size.y, rhs: 4.0f);
4106 SetNextWindowSize(size);
4107
4108 // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
4109 char title[256];
4110 if (name)
4111 ImFormatString(buf: title, IM_ARRAYSIZE(title), fmt: "%s/%s_%08X", parent_window->Name, name, id);
4112 else
4113 ImFormatString(buf: title, IM_ARRAYSIZE(title), fmt: "%s/%08X", parent_window->Name, id);
4114
4115 const float backup_border_size = g.Style.ChildBorderSize;
4116 if (!border)
4117 g.Style.ChildBorderSize = 0.0f;
4118 bool ret = Begin(name: title, NULL, flags);
4119 g.Style.ChildBorderSize = backup_border_size;
4120
4121 ImGuiWindow* child_window = g.CurrentWindow;
4122 child_window->ChildId = id;
4123 child_window->AutoFitChildAxises = auto_fit_axises;
4124
4125 // Process navigation-in immediately so NavInit can run on first frame
4126 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4127 {
4128 FocusWindow(window: child_window);
4129 NavInitWindow(window: child_window, force_reinit: false);
4130 SetActiveID(id: id+1, window: child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4131 g.ActiveIdSource = ImGuiInputSource_Nav;
4132 }
4133 return ret;
4134}
4135
4136bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4137{
4138 ImGuiWindow* window = GetCurrentWindow();
4139 return BeginChildEx(name: str_id, id: window->GetID(str: str_id), size_arg, border, flags: extra_flags);
4140}
4141
4142bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4143{
4144 IM_ASSERT(id != 0);
4145 return BeginChildEx(NULL, id, size_arg, border, flags: extra_flags);
4146}
4147
4148void ImGui::EndChild()
4149{
4150 ImGuiContext& g = *GImGui;
4151 ImGuiWindow* window = g.CurrentWindow;
4152
4153 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
4154 if (window->BeginCount > 1)
4155 {
4156 End();
4157 }
4158 else
4159 {
4160 ImVec2 sz = window->Size;
4161 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4162 sz.x = ImMax(lhs: 4.0f, rhs: sz.x);
4163 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4164 sz.y = ImMax(lhs: 4.0f, rhs: sz.y);
4165 End();
4166
4167 ImGuiWindow* parent_window = g.CurrentWindow;
4168 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4169 ItemSize(size: sz);
4170 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4171 {
4172 ItemAdd(bb, id: window->ChildId);
4173 RenderNavHighlight(bb, id: window->ChildId);
4174
4175 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4176 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4177 RenderNavHighlight(bb: ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), id: g.NavId, flags: ImGuiNavHighlightFlags_TypeThin);
4178 }
4179 else
4180 {
4181 // Not navigable into
4182 ItemAdd(bb, id: 0);
4183 }
4184 }
4185}
4186
4187// Helper to create a child window / scrolling region that looks like a normal widget frame.
4188bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4189{
4190 ImGuiContext& g = *GImGui;
4191 const ImGuiStyle& style = g.Style;
4192 PushStyleColor(idx: ImGuiCol_ChildBg, col: style.Colors[ImGuiCol_FrameBg]);
4193 PushStyleVar(idx: ImGuiStyleVar_ChildRounding, val: style.FrameRounding);
4194 PushStyleVar(idx: ImGuiStyleVar_ChildBorderSize, val: style.FrameBorderSize);
4195 PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: style.FramePadding);
4196 bool ret = BeginChild(id, size_arg: size, border: true, extra_flags: ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4197 PopStyleVar(count: 3);
4198 PopStyleColor();
4199 return ret;
4200}
4201
4202void ImGui::EndChildFrame()
4203{
4204 EndChild();
4205}
4206
4207// Save and compare stack sizes on Begin()/End() to detect usage errors
4208static void CheckStacksSize(ImGuiWindow* window, bool write)
4209{
4210 // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
4211 ImGuiContext& g = *GImGui;
4212 int* p_backup = &window->DC.StackSizesBackup[0];
4213 { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
4214 { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
4215 { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4216 // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
4217 { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
4218 { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
4219 { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
4220 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4221}
4222
4223static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4224{
4225 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4226 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4227 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4228}
4229
4230ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4231{
4232 ImGuiContext& g = *GImGui;
4233 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(key: id);
4234}
4235
4236ImGuiWindow* ImGui::FindWindowByName(const char* name)
4237{
4238 ImGuiID id = ImHash(data: name, data_size: 0);
4239 return FindWindowByID(id);
4240}
4241
4242static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4243{
4244 ImGuiContext& g = *GImGui;
4245
4246 // Create window the first time
4247 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4248 window->Flags = flags;
4249 g.WindowsById.SetVoidPtr(key: window->ID, val: window);
4250
4251 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4252 window->Pos = ImVec2(60, 60);
4253
4254 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4255 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4256 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(id: window->ID))
4257 {
4258 // Retrieve settings from .ini file
4259 window->SettingsIdx = g.SettingsWindows.index_from_pointer(it: settings);
4260 SetWindowConditionAllowFlags(window, flags: ImGuiCond_FirstUseEver, enabled: false);
4261 window->Pos = ImFloor(v: settings->Pos);
4262 window->Collapsed = settings->Collapsed;
4263 if (ImLengthSqr(lhs: settings->Size) > 0.00001f)
4264 size = ImFloor(v: settings->Size);
4265 }
4266 window->Size = window->SizeFull = window->SizeFullAtLastBegin = ImFloor(v: size);
4267 window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4268
4269 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4270 {
4271 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4272 window->AutoFitOnlyGrows = false;
4273 }
4274 else
4275 {
4276 if (window->Size.x <= 0.0f)
4277 window->AutoFitFramesX = 2;
4278 if (window->Size.y <= 0.0f)
4279 window->AutoFitFramesY = 2;
4280 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4281 }
4282
4283 g.WindowsFocusOrder.push_back(v: window);
4284 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4285 g.Windows.push_front(v: window); // Quite slow but rare and only once
4286 else
4287 g.Windows.push_back(v: window);
4288 return window;
4289}
4290
4291static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4292{
4293 ImGuiContext& g = *GImGui;
4294 if (g.NextWindowData.SizeConstraintCond != 0)
4295 {
4296 // Using -1,-1 on either X/Y axis to preserve the current size.
4297 ImRect cr = g.NextWindowData.SizeConstraintRect;
4298 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(v: new_size.x, mn: cr.Min.x, mx: cr.Max.x) : window->SizeFull.x;
4299 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(v: new_size.y, mn: cr.Min.y, mx: cr.Max.y) : window->SizeFull.y;
4300 if (g.NextWindowData.SizeCallback)
4301 {
4302 ImGuiSizeCallbackData data;
4303 data.UserData = g.NextWindowData.SizeCallbackUserData;
4304 data.Pos = window->Pos;
4305 data.CurrentSize = window->SizeFull;
4306 data.DesiredSize = new_size;
4307 g.NextWindowData.SizeCallback(&data);
4308 new_size = data.DesiredSize;
4309 }
4310 }
4311
4312 // Minimum size
4313 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4314 {
4315 new_size = ImMax(lhs: new_size, rhs: g.Style.WindowMinSize);
4316 new_size.y = ImMax(lhs: new_size.y, rhs: window->TitleBarHeight() + window->MenuBarHeight() + ImMax(lhs: 0.0f, rhs: g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4317 }
4318 return new_size;
4319}
4320
4321static ImVec2 CalcSizeContents(ImGuiWindow* window)
4322{
4323 ImVec2 sz;
4324 sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4325 sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4326 return sz + window->WindowPadding;
4327}
4328
4329static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4330{
4331 ImGuiContext& g = *GImGui;
4332 ImGuiStyle& style = g.Style;
4333 if (window->Flags & ImGuiWindowFlags_Tooltip)
4334 {
4335 // Tooltip always resize
4336 return size_contents;
4337 }
4338 else
4339 {
4340 // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4341 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4342 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4343 ImVec2 size_min = style.WindowMinSize;
4344 if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
4345 size_min = ImMin(lhs: size_min, rhs: ImVec2(4.0f, 4.0f));
4346 ImVec2 size_auto_fit = ImClamp(v: size_contents, mn: size_min, mx: ImMax(lhs: size_min, rhs: g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4347 ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, new_size: size_auto_fit);
4348 if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4349 size_auto_fit.y += style.ScrollbarSize;
4350 if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4351 size_auto_fit.x += style.ScrollbarSize;
4352 return size_auto_fit;
4353 }
4354}
4355
4356ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4357{
4358 ImVec2 size_contents = CalcSizeContents(window);
4359 return CalcSizeAfterConstraint(window, new_size: CalcSizeAutoFit(window, size_contents));
4360}
4361
4362float ImGui::GetWindowScrollMaxX(ImGuiWindow* window)
4363{
4364 return ImMax(lhs: 0.0f, rhs: window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4365}
4366
4367float ImGui::GetWindowScrollMaxY(ImGuiWindow* window)
4368{
4369 return ImMax(lhs: 0.0f, rhs: window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4370}
4371
4372static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4373{
4374 ImGuiContext& g = *GImGui;
4375 ImVec2 scroll = window->Scroll;
4376 if (window->ScrollTarget.x < FLT_MAX)
4377 {
4378 float cr_x = window->ScrollTargetCenterRatio.x;
4379 scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4380 }
4381 if (window->ScrollTarget.y < FLT_MAX)
4382 {
4383 // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
4384 float cr_y = window->ScrollTargetCenterRatio.y;
4385 float target_y = window->ScrollTarget.y;
4386 if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4387 target_y = 0.0f;
4388 if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4389 target_y = window->SizeContents.y;
4390 scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4391 }
4392 scroll = ImMax(lhs: scroll, rhs: ImVec2(0.0f, 0.0f));
4393 if (!window->Collapsed && !window->SkipItems)
4394 {
4395 scroll.x = ImMin(lhs: scroll.x, rhs: ImGui::GetWindowScrollMaxX(window));
4396 scroll.y = ImMin(lhs: scroll.y, rhs: ImGui::GetWindowScrollMaxY(window));
4397 }
4398 return scroll;
4399}
4400
4401static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4402{
4403 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4404 return ImGuiCol_PopupBg;
4405 if (flags & ImGuiWindowFlags_ChildWindow)
4406 return ImGuiCol_ChildBg;
4407 return ImGuiCol_WindowBg;
4408}
4409
4410static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4411{
4412 ImVec2 pos_min = ImLerp(a: corner_target, b: window->Pos, t: corner_norm); // Expected window upper-left
4413 ImVec2 pos_max = ImLerp(a: window->Pos + window->Size, b: corner_target, t: corner_norm); // Expected window lower-right
4414 ImVec2 size_expected = pos_max - pos_min;
4415 ImVec2 size_constrained = CalcSizeAfterConstraint(window, new_size: size_expected);
4416 *out_pos = pos_min;
4417 if (corner_norm.x == 0.0f)
4418 out_pos->x -= (size_constrained.x - size_expected.x);
4419 if (corner_norm.y == 0.0f)
4420 out_pos->y -= (size_constrained.y - size_expected.y);
4421 *out_size = size_constrained;
4422}
4423
4424struct ImGuiResizeGripDef
4425{
4426 ImVec2 CornerPos;
4427 ImVec2 InnerDir;
4428 int AngleMin12, AngleMax12;
4429};
4430
4431const ImGuiResizeGripDef resize_grip_def[4] =
4432{
4433 { .CornerPos: ImVec2(1,1), .InnerDir: ImVec2(-1,-1), .AngleMin12: 0, .AngleMax12: 3 }, // Lower right
4434 { .CornerPos: ImVec2(0,1), .InnerDir: ImVec2(+1,-1), .AngleMin12: 3, .AngleMax12: 6 }, // Lower left
4435 { .CornerPos: ImVec2(0,0), .InnerDir: ImVec2(+1,+1), .AngleMin12: 6, .AngleMax12: 9 }, // Upper left
4436 { .CornerPos: ImVec2(1,0), .InnerDir: ImVec2(-1,+1), .AngleMin12: 9,.AngleMax12: 12 }, // Upper right
4437};
4438
4439static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4440{
4441 ImRect rect = window->Rect();
4442 if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4443 if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness);
4444 if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding);
4445 if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness);
4446 if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding);
4447 IM_ASSERT(0);
4448 return ImRect();
4449}
4450
4451// Handle resize for: Resize Grips, Borders, Gamepad
4452static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4453{
4454 ImGuiContext& g = *GImGui;
4455 ImGuiWindowFlags flags = window->Flags;
4456 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4457 return;
4458 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit Debug window.
4459 return;
4460
4461 const int resize_border_count = g.IO.ConfigResizeWindowsFromEdges ? 4 : 0;
4462 const float grip_draw_size = (float)(int)ImMax(lhs: g.FontSize * 1.35f, rhs: window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4463 const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f);
4464 const float grip_hover_outer_size = g.IO.ConfigResizeWindowsFromEdges ? RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS : 0.0f;
4465
4466 ImVec2 pos_target(FLT_MAX, FLT_MAX);
4467 ImVec2 size_target(FLT_MAX, FLT_MAX);
4468
4469 // Manual resize grips
4470 PushID(str_id: "#RESIZE");
4471 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4472 {
4473 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4474 const ImVec2 corner = ImLerp(a: window->Pos, b: window->Pos + window->Size, t: grip.CornerPos);
4475
4476 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4477 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4478 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(a&: resize_rect.Min.x, b&: resize_rect.Max.x);
4479 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(a&: resize_rect.Min.y, b&: resize_rect.Max.y);
4480 bool hovered, held;
4481 ButtonBehavior(bb: resize_rect, id: window->GetID(ptr: (void*)(intptr_t)resize_grip_n), out_hovered: &hovered, out_held: &held, flags: ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4482 //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4483 if (hovered || held)
4484 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4485
4486 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4487 {
4488 // Manual auto-fit when double-clicking
4489 size_target = CalcSizeAfterConstraint(window, new_size: size_auto_fit);
4490 ClearActiveID();
4491 }
4492 else if (held)
4493 {
4494 // Resize from any of the four corners
4495 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4496 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(a: grip.InnerDir * grip_hover_outer_size, b: grip.InnerDir * -grip_hover_inner_size, t: grip.CornerPos); // Corner of the window corresponding to our corner grip
4497 CalcResizePosSizeFromAnyCorner(window, corner_target, corner_norm: grip.CornerPos, out_pos: &pos_target, out_size: &size_target);
4498 }
4499 if (resize_grip_n == 0 || held || hovered)
4500 resize_grip_col[resize_grip_n] = GetColorU32(idx: held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4501 }
4502 for (int border_n = 0; border_n < resize_border_count; border_n++)
4503 {
4504 bool hovered, held;
4505 ImRect border_rect = GetResizeBorderRect(window, border_n, perp_padding: grip_hover_inner_size, thickness: RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS);
4506 ButtonBehavior(bb: border_rect, id: window->GetID(ptr: (void*)(intptr_t)(border_n + 4)), out_hovered: &hovered, out_held: &held, flags: ImGuiButtonFlags_FlattenChildren);
4507 //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4508 if ((hovered && g.HoveredIdTimer > RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER) || held)
4509 {
4510 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4511 if (held) *border_held = border_n;
4512 }
4513 if (held)
4514 {
4515 ImVec2 border_target = window->Pos;
4516 ImVec2 border_posn;
4517 if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4518 if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4519 if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4520 if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4521 CalcResizePosSizeFromAnyCorner(window, corner_target: border_target, corner_norm: border_posn, out_pos: &pos_target, out_size: &size_target);
4522 }
4523 }
4524 PopID();
4525
4526 // Navigation resize (keyboard/gamepad)
4527 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4528 {
4529 ImVec2 nav_resize_delta;
4530 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4531 nav_resize_delta = GetNavInputAmount2d(dir_sources: ImGuiNavDirSourceFlags_Keyboard, mode: ImGuiInputReadMode_Down);
4532 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4533 nav_resize_delta = GetNavInputAmount2d(dir_sources: ImGuiNavDirSourceFlags_PadDPad, mode: ImGuiInputReadMode_Down);
4534 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4535 {
4536 const float NAV_RESIZE_SPEED = 600.0f;
4537 nav_resize_delta *= ImFloor(f: NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(lhs: g.IO.DisplayFramebufferScale.x, rhs: g.IO.DisplayFramebufferScale.y));
4538 g.NavWindowingToggleLayer = false;
4539 g.NavDisableMouseHover = true;
4540 resize_grip_col[0] = GetColorU32(idx: ImGuiCol_ResizeGripActive);
4541 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4542 size_target = CalcSizeAfterConstraint(window, new_size: window->SizeFull + nav_resize_delta);
4543 }
4544 }
4545
4546 // Apply back modified position/size to window
4547 if (size_target.x != FLT_MAX)
4548 {
4549 window->SizeFull = size_target;
4550 MarkIniSettingsDirty(window);
4551 }
4552 if (pos_target.x != FLT_MAX)
4553 {
4554 window->Pos = ImFloor(v: pos_target);
4555 MarkIniSettingsDirty(window);
4556 }
4557
4558 window->Size = window->SizeFull;
4559}
4560
4561void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4562{
4563 window->ParentWindow = parent_window;
4564 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4565 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4566 window->RootWindow = parent_window->RootWindow;
4567 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4568 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4569 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4570 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4571}
4572
4573// Push a new ImGui window to add widgets to.
4574// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
4575// - Begin/End can be called multiple times during the frame with the same window name to append content.
4576// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4577// You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
4578// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
4579// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
4580bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4581{
4582 ImGuiContext& g = *GImGui;
4583 const ImGuiStyle& style = g.Style;
4584 IM_ASSERT(name != NULL); // Window name required
4585 IM_ASSERT(g.FrameScopeActive); // Forgot to call ImGui::NewFrame()
4586 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4587
4588 // Find or create
4589 ImGuiWindow* window = FindWindowByName(name);
4590 const bool window_just_created = (window == NULL);
4591 if (window_just_created)
4592 {
4593 ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
4594 window = CreateNewWindow(name, size: size_on_first_use, flags);
4595 }
4596
4597 // Automatically disable manual moving/resizing when NoInputs is set
4598 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
4599 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4600
4601 if (flags & ImGuiWindowFlags_NavFlattened)
4602 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4603
4604 const int current_frame = g.FrameCount;
4605 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4606 if (first_begin_of_the_frame)
4607 window->Flags = (ImGuiWindowFlags)flags;
4608 else
4609 flags = window->Flags;
4610
4611 // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
4612 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4613 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4614 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4615 window->HasCloseButton = (p_open != NULL);
4616
4617 // Update the Appearing flag
4618 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4619 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4620 if (flags & ImGuiWindowFlags_Popup)
4621 {
4622 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4623 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4624 window_just_activated_by_user |= (window != popup_ref.Window);
4625 }
4626 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4627 if (window->Appearing)
4628 SetWindowConditionAllowFlags(window, flags: ImGuiCond_Appearing, enabled: true);
4629
4630 // Add to stack
4631 g.CurrentWindowStack.push_back(v: window);
4632 SetCurrentWindow(window);
4633 CheckStacksSize(window, write: true);
4634 if (flags & ImGuiWindowFlags_Popup)
4635 {
4636 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4637 popup_ref.Window = window;
4638 g.CurrentPopupStack.push_back(v: popup_ref);
4639 window->PopupId = popup_ref.PopupId;
4640 }
4641
4642 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4643 window->NavLastIds[0] = 0;
4644
4645 // Process SetNextWindow***() calls
4646 bool window_pos_set_by_api = false;
4647 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4648 if (g.NextWindowData.PosCond)
4649 {
4650 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4651 if (window_pos_set_by_api && ImLengthSqr(lhs: g.NextWindowData.PosPivotVal) > 0.00001f)
4652 {
4653 // May be processed on the next frame if this is our first frame and we are measuring size
4654 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4655 window->SetWindowPosVal = g.NextWindowData.PosVal;
4656 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4657 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4658 }
4659 else
4660 {
4661 SetWindowPos(window, pos: g.NextWindowData.PosVal, cond: g.NextWindowData.PosCond);
4662 }
4663 }
4664 if (g.NextWindowData.SizeCond)
4665 {
4666 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4667 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4668 SetWindowSize(window, size: g.NextWindowData.SizeVal, cond: g.NextWindowData.SizeCond);
4669 }
4670 if (g.NextWindowData.ContentSizeCond)
4671 {
4672 // Adjust passed "client size" to become a "window size"
4673 window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4674 if (window->SizeContentsExplicit.y != 0.0f)
4675 window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4676 }
4677 else if (first_begin_of_the_frame)
4678 {
4679 window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4680 }
4681 if (g.NextWindowData.CollapsedCond)
4682 SetWindowCollapsed(window, collapsed: g.NextWindowData.CollapsedVal, cond: g.NextWindowData.CollapsedCond);
4683 if (g.NextWindowData.FocusCond)
4684 FocusWindow(window);
4685 if (window->Appearing)
4686 SetWindowConditionAllowFlags(window, flags: ImGuiCond_Appearing, enabled: false);
4687
4688 // When reusing window again multiple times a frame, just append content (don't need to setup again)
4689 if (first_begin_of_the_frame)
4690 {
4691 // Initialize
4692 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4693 UpdateWindowParentAndRootLinks(window, flags, parent_window);
4694
4695 window->Active = true;
4696 window->BeginOrderWithinParent = 0;
4697 window->BeginOrderWithinContext = g.WindowsActiveCount++;
4698 window->BeginCount = 0;
4699 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4700 window->LastFrameActive = current_frame;
4701 window->IDStack.resize(new_size: 1);
4702
4703 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
4704
4705 // Update contents size from last frame for auto-fitting (or use explicit size)
4706 window->SizeContents = CalcSizeContents(window);
4707 if (window->HiddenFramesRegular > 0)
4708 window->HiddenFramesRegular--;
4709 if (window->HiddenFramesForResize > 0)
4710 window->HiddenFramesForResize--;
4711
4712 // Hide new windows for one frame until they calculate their size
4713 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
4714 window->HiddenFramesForResize = 1;
4715
4716 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
4717 // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
4718 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
4719 {
4720 window->HiddenFramesForResize = 1;
4721 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4722 {
4723 if (!window_size_x_set_by_api)
4724 window->Size.x = window->SizeFull.x = 0.f;
4725 if (!window_size_y_set_by_api)
4726 window->Size.y = window->SizeFull.y = 0.f;
4727 window->SizeContents = ImVec2(0.f, 0.f);
4728 }
4729 }
4730
4731 SetCurrentWindow(window);
4732
4733 // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
4734 window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4735 window->WindowPadding = style.WindowPadding;
4736 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4737 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4738 window->DC.MenuBarOffset.x = ImMax(lhs: ImMax(lhs: window->WindowPadding.x, rhs: style.ItemSpacing.x), rhs: g.NextWindowData.MenuBarOffsetMinVal.x);
4739 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
4740
4741 // Collapse window by double-clicking on title bar
4742 // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
4743 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4744 {
4745 // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
4746 ImRect title_bar_rect = window->TitleBarRect();
4747 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(r_min: title_bar_rect.Min, r_max: title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4748 window->WantCollapseToggle = true;
4749 if (window->WantCollapseToggle)
4750 {
4751 window->Collapsed = !window->Collapsed;
4752 MarkIniSettingsDirty(window);
4753 FocusWindow(window);
4754 }
4755 }
4756 else
4757 {
4758 window->Collapsed = false;
4759 }
4760 window->WantCollapseToggle = false;
4761
4762 // SIZE
4763
4764 // Calculate auto-fit size, handle automatic resize
4765 const ImVec2 size_auto_fit = CalcSizeAutoFit(window, size_contents: window->SizeContents);
4766 ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
4767 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
4768 {
4769 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
4770 if (!window_size_x_set_by_api)
4771 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
4772 if (!window_size_y_set_by_api)
4773 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
4774 }
4775 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4776 {
4777 // Auto-fit may only grow window during the first few frames
4778 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4779 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
4780 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(lhs: window->SizeFull.x, rhs: size_auto_fit.x) : size_auto_fit.x;
4781 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
4782 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(lhs: window->SizeFull.y, rhs: size_auto_fit.y) : size_auto_fit.y;
4783 if (!window->Collapsed)
4784 MarkIniSettingsDirty(window);
4785 }
4786
4787 // Apply minimum/maximum window size constraints and final size
4788 window->SizeFull = CalcSizeAfterConstraint(window, new_size: window->SizeFull);
4789 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
4790
4791 // SCROLLBAR STATUS
4792
4793 // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
4794 if (!window->Collapsed)
4795 {
4796 // When reading the current size we need to read it after size constraints have been applied
4797 float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
4798 float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
4799 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
4800 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4801 if (window->ScrollbarX && !window->ScrollbarY)
4802 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4803 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4804 }
4805
4806 // POSITION
4807
4808 // Popup latch its initial position, will position itself when it appears next frame
4809 if (window_just_activated_by_user)
4810 {
4811 window->AutoPosLastDirection = ImGuiDir_None;
4812 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4813 window->Pos = g.CurrentPopupStack.back().OpenPopupPos;
4814 }
4815
4816 // Position child window
4817 if (flags & ImGuiWindowFlags_ChildWindow)
4818 {
4819 window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
4820 parent_window->DC.ChildWindows.push_back(v: window);
4821 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
4822 window->Pos = parent_window->DC.CursorPos;
4823 }
4824
4825 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
4826 if (window_pos_with_pivot)
4827 SetWindowPos(window, pos: ImMax(lhs: style.DisplaySafeAreaPadding, rhs: window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), cond: 0); // Position given a pivot (e.g. for centering)
4828 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
4829 window->Pos = FindBestWindowPosForPopup(window);
4830 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
4831 window->Pos = FindBestWindowPosForPopup(window);
4832 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
4833 window->Pos = FindBestWindowPosForPopup(window);
4834
4835 // Clamp position so it stays visible
4836 if (!(flags & ImGuiWindowFlags_ChildWindow))
4837 {
4838 if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
4839 {
4840 ImVec2 padding = ImMax(lhs: style.DisplayWindowPadding, rhs: style.DisplaySafeAreaPadding);
4841 window->Pos = ImMax(lhs: window->Pos + window->Size, rhs: padding) - window->Size;
4842 window->Pos = ImMin(lhs: window->Pos, rhs: g.IO.DisplaySize - padding);
4843 }
4844 }
4845 window->Pos = ImFloor(v: window->Pos);
4846
4847 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
4848 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
4849
4850 // Prepare for item focus requests
4851 window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4852 window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4853 window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4854 window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4855
4856 // Apply scrolling
4857 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, snap_on_edges: true);
4858 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4859
4860 // Apply window focus (new and reactivated windows are moved to front)
4861 bool want_focus = false;
4862 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4863 if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4864 want_focus = true;
4865
4866 // Handle manual resize: Resize Grips, Borders, Gamepad
4867 int border_held = -1;
4868 ImU32 resize_grip_col[4] = { 0 };
4869 const int resize_grip_count = g.IO.ConfigResizeWindowsFromEdges ? 2 : 1; // 4
4870 const float grip_draw_size = (float)(int)ImMax(lhs: g.FontSize * 1.35f, rhs: window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4871 if (!window->Collapsed)
4872 UpdateManualResize(window, size_auto_fit, border_held: &border_held, resize_grip_count, resize_grip_col: &resize_grip_col[0]);
4873
4874 // Default item width. Make it proportional to window size if window manually resizes
4875 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4876 window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4877 else
4878 window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4879
4880 // DRAWING
4881
4882 // Setup draw list and outer clipping rectangle
4883 window->DrawList->Clear();
4884 window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
4885 window->DrawList->PushTextureID(texture_id: g.Font->ContainerAtlas->TexID);
4886 ImRect viewport_rect(GetViewportRect());
4887 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
4888 PushClipRect(clip_rect_min: parent_window->ClipRect.Min, clip_rect_max: parent_window->ClipRect.Max, intersect_with_current_clip_rect: true);
4889 else
4890 PushClipRect(clip_rect_min: viewport_rect.Min, clip_rect_max: viewport_rect.Max, intersect_with_current_clip_rect: true);
4891
4892 // Draw modal window background (darkens what is behind them, all viewports)
4893 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
4894 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
4895 if (dim_bg_for_modal || dim_bg_for_window_list)
4896 {
4897 const ImU32 dim_bg_col = GetColorU32(idx: dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, alpha_mul: g.DimBgRatio);
4898 window->DrawList->AddRectFilled(a: viewport_rect.Min, b: viewport_rect.Max, col: dim_bg_col);
4899 }
4900
4901 // Draw navigation selection/windowing rectangle background
4902 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
4903 {
4904 ImRect bb = window->Rect();
4905 bb.Expand(amount: g.FontSize);
4906 if (!bb.Contains(r: viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
4907 window->DrawList->AddRectFilled(a: bb.Min, b: bb.Max, col: GetColorU32(idx: ImGuiCol_NavWindowingHighlight, alpha_mul: g.NavWindowingHighlightAlpha * 0.25f), rounding: g.Style.WindowRounding);
4908 }
4909
4910 // Draw window + handle manual resize
4911 const float window_rounding = window->WindowRounding;
4912 const float window_border_size = window->WindowBorderSize;
4913 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
4914 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
4915 const ImRect title_bar_rect = window->TitleBarRect();
4916 if (window->Collapsed)
4917 {
4918 // Title bar only
4919 float backup_border_size = style.FrameBorderSize;
4920 g.Style.FrameBorderSize = window->WindowBorderSize;
4921 ImU32 title_bar_col = GetColorU32(idx: (title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
4922 RenderFrame(p_min: title_bar_rect.Min, p_max: title_bar_rect.Max, fill_col: title_bar_col, border: true, rounding: window_rounding);
4923 g.Style.FrameBorderSize = backup_border_size;
4924 }
4925 else
4926 {
4927 // Window background
4928 if (!(flags & ImGuiWindowFlags_NoBackground))
4929 {
4930 ImU32 bg_col = GetColorU32(idx: GetWindowBgColorIdxFromFlags(flags));
4931 if (g.NextWindowData.BgAlphaCond != 0)
4932 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
4933 window->DrawList->AddRectFilled(a: window->Pos + ImVec2(0, window->TitleBarHeight()), b: window->Pos + window->Size, col: bg_col, rounding: window_rounding, rounding_corners_flags: (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
4934 }
4935 g.NextWindowData.BgAlphaCond = 0;
4936
4937 // Title bar
4938 ImU32 title_bar_col = GetColorU32(idx: window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
4939 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4940 window->DrawList->AddRectFilled(a: title_bar_rect.Min, b: title_bar_rect.Max, col: title_bar_col, rounding: window_rounding, rounding_corners_flags: ImDrawCornerFlags_Top);
4941
4942 // Menu bar
4943 if (flags & ImGuiWindowFlags_MenuBar)
4944 {
4945 ImRect menu_bar_rect = window->MenuBarRect();
4946 menu_bar_rect.ClipWith(r: window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
4947 window->DrawList->AddRectFilled(a: menu_bar_rect.Min, b: menu_bar_rect.Max, col: GetColorU32(idx: ImGuiCol_MenuBarBg), rounding: (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, rounding_corners_flags: ImDrawCornerFlags_Top);
4948 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
4949 window->DrawList->AddLine(a: menu_bar_rect.GetBL(), b: menu_bar_rect.GetBR(), col: GetColorU32(idx: ImGuiCol_Border), thickness: style.FrameBorderSize);
4950 }
4951
4952 // Scrollbars
4953 if (window->ScrollbarX)
4954 Scrollbar(direction: ImGuiLayoutType_Horizontal);
4955 if (window->ScrollbarY)
4956 Scrollbar(direction: ImGuiLayoutType_Vertical);
4957
4958 // Render resize grips (after their input handling so we don't have a frame of latency)
4959 if (!(flags & ImGuiWindowFlags_NoResize))
4960 {
4961 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4962 {
4963 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4964 const ImVec2 corner = ImLerp(a: window->Pos, b: window->Pos + window->Size, t: grip.CornerPos);
4965 window->DrawList->PathLineTo(pos: corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
4966 window->DrawList->PathLineTo(pos: corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
4967 window->DrawList->PathArcToFast(centre: ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), radius: window_rounding, a_min_of_12: grip.AngleMin12, a_max_of_12: grip.AngleMax12);
4968 window->DrawList->PathFillConvex(col: resize_grip_col[resize_grip_n]);
4969 }
4970 }
4971
4972 // Borders
4973 if (window_border_size > 0.0f && !(flags & ImGuiWindowFlags_NoBackground))
4974 window->DrawList->AddRect(a: window->Pos, b: window->Pos + window->Size, col: GetColorU32(idx: ImGuiCol_Border), rounding: window_rounding, rounding_corners_flags: ImDrawCornerFlags_All, thickness: window_border_size);
4975 if (border_held != -1)
4976 {
4977 ImRect border = GetResizeBorderRect(window, border_n: border_held, perp_padding: grip_draw_size, thickness: 0.0f);
4978 window->DrawList->AddLine(a: border.Min, b: border.Max, col: GetColorU32(idx: ImGuiCol_SeparatorActive), thickness: ImMax(lhs: 1.0f, rhs: window_border_size));
4979 }
4980 if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
4981 window->DrawList->AddLine(a: title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), b: title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), col: GetColorU32(idx: ImGuiCol_Border), thickness: style.FrameBorderSize);
4982 }
4983
4984 // Draw navigation selection/windowing rectangle border
4985 if (g.NavWindowingTargetAnim == window)
4986 {
4987 float rounding = ImMax(lhs: window->WindowRounding, rhs: g.Style.WindowRounding);
4988 ImRect bb = window->Rect();
4989 bb.Expand(amount: g.FontSize);
4990 if (bb.Contains(r: viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
4991 {
4992 bb.Expand(amount: -g.FontSize - 1.0f);
4993 rounding = window->WindowRounding;
4994 }
4995 window->DrawList->AddRect(a: bb.Min, b: bb.Max, col: GetColorU32(idx: ImGuiCol_NavWindowingHighlight, alpha_mul: g.NavWindowingHighlightAlpha), rounding, rounding_corners_flags: ~0, thickness: 3.0f);
4996 }
4997
4998 // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
4999 window->SizeFullAtLastBegin = window->SizeFull;
5000
5001 // Update various regions. Variables they depends on are set above in this function.
5002 // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5003 window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5004 window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
5005 window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
5006 window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
5007
5008 // Setup drawing context
5009 // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
5010 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5011 window->DC.GroupOffset.x = 0.0f;
5012 window->DC.ColumnsOffset.x = 0.0f;
5013 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
5014 window->DC.CursorPos = window->DC.CursorStartPos;
5015 window->DC.CursorPosPrevLine = window->DC.CursorPos;
5016 window->DC.CursorMaxPos = window->DC.CursorStartPos;
5017 window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5018 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5019 window->DC.NavHideHighlightOneFrame = false;
5020 window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f);
5021 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5022 window->DC.NavLayerActiveMaskNext = 0x00;
5023 window->DC.MenuBarAppending = false;
5024 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
5025 window->DC.ChildWindows.resize(new_size: 0);
5026 window->DC.LayoutType = ImGuiLayoutType_Vertical;
5027 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5028 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5029 window->DC.ItemWidth = window->ItemWidthDefault;
5030 window->DC.TextWrapPos = -1.0f; // disabled
5031 window->DC.ItemFlagsStack.resize(new_size: 0);
5032 window->DC.ItemWidthStack.resize(new_size: 0);
5033 window->DC.TextWrapPosStack.resize(new_size: 0);
5034 window->DC.ColumnsSet = NULL;
5035 window->DC.TreeDepth = 0;
5036 window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
5037 window->DC.StateStorage = &window->StateStorage;
5038 window->DC.GroupStack.resize(new_size: 0);
5039 window->MenuColumns.Update(count: 3, spacing: style.ItemSpacing.x, clear: window_just_activated_by_user);
5040
5041 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5042 {
5043 window->DC.ItemFlags = parent_window->DC.ItemFlags;
5044 window->DC.ItemFlagsStack.push_back(v: window->DC.ItemFlags);
5045 }
5046
5047 if (window->AutoFitFramesX > 0)
5048 window->AutoFitFramesX--;
5049 if (window->AutoFitFramesY > 0)
5050 window->AutoFitFramesY--;
5051
5052 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5053 if (want_focus)
5054 {
5055 FocusWindow(window);
5056 NavInitWindow(window, force_reinit: false);
5057 }
5058
5059 // Title bar
5060 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5061 {
5062 // Close & collapse button are on layer 1 (same as menus) and don't default focus
5063 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5064 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5065 window->DC.NavLayerCurrent++;
5066 window->DC.NavLayerCurrentMask <<= 1;
5067
5068 // Collapse button
5069 if (!(flags & ImGuiWindowFlags_NoCollapse))
5070 if (CollapseButton(id: window->GetID(str: "#COLLAPSE"), pos: window->Pos))
5071 window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
5072
5073 // Close button
5074 if (p_open != NULL)
5075 {
5076 const float pad = style.FramePadding.y;
5077 const float rad = g.FontSize * 0.5f;
5078 if (CloseButton(id: window->GetID(str: "#CLOSE"), pos: window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), radius: rad + 1))
5079 *p_open = false;
5080 }
5081
5082 window->DC.NavLayerCurrent--;
5083 window->DC.NavLayerCurrentMask >>= 1;
5084 window->DC.ItemFlags = item_flags_backup;
5085
5086 // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.)
5087 ImVec2 text_size = CalcTextSize(text: name, NULL, hide_text_after_double_hash: true);
5088 ImRect text_r = title_bar_rect;
5089 float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5090 float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5091 if (style.WindowTitleAlign.x > 0.0f)
5092 pad_right = ImLerp(a: pad_right, b: pad_left, t: style.WindowTitleAlign.x);
5093 text_r.Min.x += pad_left;
5094 text_r.Max.x -= pad_right;
5095 ImRect clip_rect = text_r;
5096 clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton()
5097 RenderTextClipped(pos_min: text_r.Min, pos_max: text_r.Max, text: name, NULL, text_size_if_known: &text_size, align: style.WindowTitleAlign, clip_rect: &clip_rect);
5098 }
5099
5100 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5101 window->OuterRectClipped = window->Rect();
5102 window->OuterRectClipped.ClipWith(r: window->ClipRect);
5103
5104 // Pressing CTRL+C while holding on a window copy its content to the clipboard
5105 // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
5106 // Maybe we can support CTRL+C on every element?
5107 /*
5108 if (g.ActiveId == move_id)
5109 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5110 LogToClipboard();
5111 */
5112
5113 // Inner rectangle
5114 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5115 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5116 window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5117 window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5118 window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5119 window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5120 //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5121
5122 // Inner clipping rectangle
5123 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5124 window->InnerClipRect.Min.x = ImFloor(f: 0.5f + window->InnerMainRect.Min.x + ImMax(lhs: 0.0f, rhs: ImFloor(f: window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5125 window->InnerClipRect.Min.y = ImFloor(f: 0.5f + window->InnerMainRect.Min.y);
5126 window->InnerClipRect.Max.x = ImFloor(f: 0.5f + window->InnerMainRect.Max.x - ImMax(lhs: 0.0f, rhs: ImFloor(f: window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5127 window->InnerClipRect.Max.y = ImFloor(f: 0.5f + window->InnerMainRect.Max.y);
5128
5129 // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.).
5130 window->DC.LastItemId = window->MoveId;
5131 window->DC.LastItemStatusFlags = IsMouseHoveringRect(r_min: title_bar_rect.Min, r_max: title_bar_rect.Max, clip: false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5132 window->DC.LastItemRect = title_bar_rect;
5133 }
5134
5135 PushClipRect(clip_rect_min: window->InnerClipRect.Min, clip_rect_max: window->InnerClipRect.Max, intersect_with_current_clip_rect: true);
5136
5137 // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
5138 if (first_begin_of_the_frame)
5139 window->WriteAccessed = false;
5140
5141 window->BeginCount++;
5142 g.NextWindowData.Clear();
5143
5144 if (flags & ImGuiWindowFlags_ChildWindow)
5145 {
5146 // Child window can be out of sight and have "negative" clip windows.
5147 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5148 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5149
5150 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5151 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5152 window->HiddenFramesRegular = 1;
5153
5154 // Completely hide along with parent or if parent is collapsed
5155 if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5156 window->HiddenFramesRegular = 1;
5157 }
5158
5159 // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)
5160 if (style.Alpha <= 0.0f)
5161 window->HiddenFramesRegular = 1;
5162
5163 // Update the Hidden flag
5164 window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize);
5165
5166 // Return false if we don't intend to display anything to allow user to perform an early out optimization
5167 window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5168
5169 return !window->SkipItems;
5170}
5171
5172// Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5173#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5174bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5175{
5176 // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file.
5177 if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5178 SetNextWindowSize(size: size_first_use, cond: ImGuiCond_FirstUseEver);
5179
5180 // Old API feature: override the window background alpha with a parameter.
5181 if (bg_alpha_override >= 0.0f)
5182 SetNextWindowBgAlpha(bg_alpha_override);
5183
5184 return Begin(name, p_open, flags);
5185}
5186#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5187
5188void ImGui::End()
5189{
5190 ImGuiContext& g = *GImGui;
5191 ImGuiWindow* window = g.CurrentWindow;
5192
5193 if (window->DC.ColumnsSet != NULL)
5194 EndColumns();
5195 PopClipRect(); // Inner window clip rectangle
5196
5197 // Stop logging
5198 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
5199 LogFinish();
5200
5201 // Pop from window stack
5202 g.CurrentWindowStack.pop_back();
5203 if (window->Flags & ImGuiWindowFlags_Popup)
5204 g.CurrentPopupStack.pop_back();
5205 CheckStacksSize(window, write: false);
5206 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5207}
5208
5209void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5210{
5211 ImGuiContext& g = *GImGui;
5212 if (g.WindowsFocusOrder.back() == window)
5213 return;
5214 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window
5215 if (g.WindowsFocusOrder[i] == window)
5216 {
5217 memmove(dest: &g.WindowsFocusOrder[i], src: &g.WindowsFocusOrder[i + 1], n: (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5218 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5219 break;
5220 }
5221}
5222
5223void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5224{
5225 ImGuiContext& g = *GImGui;
5226 ImGuiWindow* current_front_window = g.Windows.back();
5227 if (current_front_window == window || current_front_window->RootWindow == window)
5228 return;
5229 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5230 if (g.Windows[i] == window)
5231 {
5232 memmove(dest: &g.Windows[i], src: &g.Windows[i + 1], n: (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5233 g.Windows[g.Windows.Size - 1] = window;
5234 break;
5235 }
5236}
5237
5238void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5239{
5240 ImGuiContext& g = *GImGui;
5241 if (g.Windows[0] == window)
5242 return;
5243 for (int i = 0; i < g.Windows.Size; i++)
5244 if (g.Windows[i] == window)
5245 {
5246 memmove(dest: &g.Windows[1], src: &g.Windows[0], n: (size_t)i * sizeof(ImGuiWindow*));
5247 g.Windows[0] = window;
5248 break;
5249 }
5250}
5251
5252// Moving window to front of display and set focus (which happens to be back of our sorted list)
5253void ImGui::FocusWindow(ImGuiWindow* window)
5254{
5255 ImGuiContext& g = *GImGui;
5256
5257 if (g.NavWindow != window)
5258 {
5259 g.NavWindow = window;
5260 if (window && g.NavDisableMouseHover)
5261 g.NavMousePosDirty = true;
5262 g.NavInitRequest = false;
5263 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5264 g.NavIdIsAlive = false;
5265 g.NavLayer = 0;
5266 //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
5267 }
5268
5269 // Passing NULL allow to disable keyboard focus
5270 if (!window)
5271 return;
5272
5273 // Move the root window to the top of the pile
5274 if (window->RootWindow)
5275 window = window->RootWindow;
5276
5277 // Steal focus on active widgets
5278 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5279 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5280 ClearActiveID();
5281
5282 // Bring to front
5283 BringWindowToFocusFront(window);
5284 if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5285 BringWindowToDisplayFront(window);
5286}
5287
5288void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
5289{
5290 ImGuiContext& g = *GImGui;
5291 for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
5292 {
5293 // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
5294 ImGuiWindow* window = g.WindowsFocusOrder[i];
5295 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5296 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5297 {
5298 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5299 FocusWindow(window: focus_window);
5300 return;
5301 }
5302 }
5303}
5304
5305void ImGui::PushItemWidth(float item_width)
5306{
5307 ImGuiWindow* window = GetCurrentWindow();
5308 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5309 window->DC.ItemWidthStack.push_back(v: window->DC.ItemWidth);
5310}
5311
5312void ImGui::PushMultiItemsWidths(int components, float w_full)
5313{
5314 ImGuiWindow* window = GetCurrentWindow();
5315 const ImGuiStyle& style = GImGui->Style;
5316 if (w_full <= 0.0f)
5317 w_full = CalcItemWidth();
5318 const float w_item_one = ImMax(lhs: 1.0f, rhs: (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5319 const float w_item_last = ImMax(lhs: 1.0f, rhs: (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5320 window->DC.ItemWidthStack.push_back(v: w_item_last);
5321 for (int i = 0; i < components-1; i++)
5322 window->DC.ItemWidthStack.push_back(v: w_item_one);
5323 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5324}
5325
5326void ImGui::PopItemWidth()
5327{
5328 ImGuiWindow* window = GetCurrentWindow();
5329 window->DC.ItemWidthStack.pop_back();
5330 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5331}
5332
5333float ImGui::CalcItemWidth()
5334{
5335 ImGuiWindow* window = GetCurrentWindowRead();
5336 float w = window->DC.ItemWidth;
5337 if (w < 0.0f)
5338 {
5339 // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
5340 float width_to_right_edge = GetContentRegionAvail().x;
5341 w = ImMax(lhs: 1.0f, rhs: width_to_right_edge + w);
5342 }
5343 w = (float)(int)w;
5344 return w;
5345}
5346
5347void ImGui::SetCurrentFont(ImFont* font)
5348{
5349 ImGuiContext& g = *GImGui;
5350 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5351 IM_ASSERT(font->Scale > 0.0f);
5352 g.Font = font;
5353 g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
5354 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5355
5356 ImFontAtlas* atlas = g.Font->ContainerAtlas;
5357 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5358 g.DrawListSharedData.Font = g.Font;
5359 g.DrawListSharedData.FontSize = g.FontSize;
5360}
5361
5362void ImGui::PushFont(ImFont* font)
5363{
5364 ImGuiContext& g = *GImGui;
5365 if (!font)
5366 font = GetDefaultFont();
5367 SetCurrentFont(font);
5368 g.FontStack.push_back(v: font);
5369 g.CurrentWindow->DrawList->PushTextureID(texture_id: font->ContainerAtlas->TexID);
5370}
5371
5372void ImGui::PopFont()
5373{
5374 ImGuiContext& g = *GImGui;
5375 g.CurrentWindow->DrawList->PopTextureID();
5376 g.FontStack.pop_back();
5377 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5378}
5379
5380void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5381{
5382 ImGuiWindow* window = GetCurrentWindow();
5383 if (enabled)
5384 window->DC.ItemFlags |= option;
5385 else
5386 window->DC.ItemFlags &= ~option;
5387 window->DC.ItemFlagsStack.push_back(v: window->DC.ItemFlags);
5388}
5389
5390void ImGui::PopItemFlag()
5391{
5392 ImGuiWindow* window = GetCurrentWindow();
5393 window->DC.ItemFlagsStack.pop_back();
5394 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5395}
5396
5397// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
5398void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5399{
5400 PushItemFlag(option: ImGuiItemFlags_NoTabStop, enabled: !allow_keyboard_focus);
5401}
5402
5403void ImGui::PopAllowKeyboardFocus()
5404{
5405 PopItemFlag();
5406}
5407
5408void ImGui::PushButtonRepeat(bool repeat)
5409{
5410 PushItemFlag(option: ImGuiItemFlags_ButtonRepeat, enabled: repeat);
5411}
5412
5413void ImGui::PopButtonRepeat()
5414{
5415 PopItemFlag();
5416}
5417
5418void ImGui::PushTextWrapPos(float wrap_pos_x)
5419{
5420 ImGuiWindow* window = GetCurrentWindow();
5421 window->DC.TextWrapPos = wrap_pos_x;
5422 window->DC.TextWrapPosStack.push_back(v: wrap_pos_x);
5423}
5424
5425void ImGui::PopTextWrapPos()
5426{
5427 ImGuiWindow* window = GetCurrentWindow();
5428 window->DC.TextWrapPosStack.pop_back();
5429 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5430}
5431
5432// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
5433void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5434{
5435 ImGuiContext& g = *GImGui;
5436 ImGuiColorMod backup;
5437 backup.Col = idx;
5438 backup.BackupValue = g.Style.Colors[idx];
5439 g.ColorModifiers.push_back(v: backup);
5440 g.Style.Colors[idx] = ColorConvertU32ToFloat4(in: col);
5441}
5442
5443void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5444{
5445 ImGuiContext& g = *GImGui;
5446 ImGuiColorMod backup;
5447 backup.Col = idx;
5448 backup.BackupValue = g.Style.Colors[idx];
5449 g.ColorModifiers.push_back(v: backup);
5450 g.Style.Colors[idx] = col;
5451}
5452
5453void ImGui::PopStyleColor(int count)
5454{
5455 ImGuiContext& g = *GImGui;
5456 while (count > 0)
5457 {
5458 ImGuiColorMod& backup = g.ColorModifiers.back();
5459 g.Style.Colors[backup.Col] = backup.BackupValue;
5460 g.ColorModifiers.pop_back();
5461 count--;
5462 }
5463}
5464
5465struct ImGuiStyleVarInfo
5466{
5467 ImGuiDataType Type;
5468 ImU32 Count;
5469 ImU32 Offset;
5470 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5471};
5472
5473static const ImGuiStyleVarInfo GStyleVarInfo[] =
5474{
5475 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
5476 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
5477 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
5478 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
5479 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
5480 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
5481 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
5482 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
5483 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
5484 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
5485 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
5486 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
5487 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
5488 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
5489 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
5490 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
5491 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
5492 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
5493 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
5494 { .Type: ImGuiDataType_Float, .Count: 1, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
5495 { .Type: ImGuiDataType_Float, .Count: 2, .Offset: (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
5496};
5497
5498static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5499{
5500 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5501 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5502 return &GStyleVarInfo[idx];
5503}
5504
5505void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5506{
5507 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5508 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5509 {
5510 ImGuiContext& g = *GImGui;
5511 float* pvar = (float*)var_info->GetVarPtr(style: &g.Style);
5512 g.StyleModifiers.push_back(v: ImGuiStyleMod(idx, *pvar));
5513 *pvar = val;
5514 return;
5515 }
5516 IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5517}
5518
5519void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5520{
5521 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5522 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5523 {
5524 ImGuiContext& g = *GImGui;
5525 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(style: &g.Style);
5526 g.StyleModifiers.push_back(v: ImGuiStyleMod(idx, *pvar));
5527 *pvar = val;
5528 return;
5529 }
5530 IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5531}
5532
5533void ImGui::PopStyleVar(int count)
5534{
5535 ImGuiContext& g = *GImGui;
5536 while (count > 0)
5537 {
5538 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5539 ImGuiStyleMod& backup = g.StyleModifiers.back();
5540 const ImGuiStyleVarInfo* info = GetStyleVarInfo(idx: backup.VarIdx);
5541 void* data = info->GetVarPtr(style: &g.Style);
5542 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
5543 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5544 g.StyleModifiers.pop_back();
5545 count--;
5546 }
5547}
5548
5549const char* ImGui::GetStyleColorName(ImGuiCol idx)
5550{
5551 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5552 switch (idx)
5553 {
5554 case ImGuiCol_Text: return "Text";
5555 case ImGuiCol_TextDisabled: return "TextDisabled";
5556 case ImGuiCol_WindowBg: return "WindowBg";
5557 case ImGuiCol_ChildBg: return "ChildBg";
5558 case ImGuiCol_PopupBg: return "PopupBg";
5559 case ImGuiCol_Border: return "Border";
5560 case ImGuiCol_BorderShadow: return "BorderShadow";
5561 case ImGuiCol_FrameBg: return "FrameBg";
5562 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5563 case ImGuiCol_FrameBgActive: return "FrameBgActive";
5564 case ImGuiCol_TitleBg: return "TitleBg";
5565 case ImGuiCol_TitleBgActive: return "TitleBgActive";
5566 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5567 case ImGuiCol_MenuBarBg: return "MenuBarBg";
5568 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5569 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5570 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5571 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5572 case ImGuiCol_CheckMark: return "CheckMark";
5573 case ImGuiCol_SliderGrab: return "SliderGrab";
5574 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5575 case ImGuiCol_Button: return "Button";
5576 case ImGuiCol_ButtonHovered: return "ButtonHovered";
5577 case ImGuiCol_ButtonActive: return "ButtonActive";
5578 case ImGuiCol_Header: return "Header";
5579 case ImGuiCol_HeaderHovered: return "HeaderHovered";
5580 case ImGuiCol_HeaderActive: return "HeaderActive";
5581 case ImGuiCol_Separator: return "Separator";
5582 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5583 case ImGuiCol_SeparatorActive: return "SeparatorActive";
5584 case ImGuiCol_ResizeGrip: return "ResizeGrip";
5585 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5586 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5587 case ImGuiCol_PlotLines: return "PlotLines";
5588 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5589 case ImGuiCol_PlotHistogram: return "PlotHistogram";
5590 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5591 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5592 case ImGuiCol_DragDropTarget: return "DragDropTarget";
5593 case ImGuiCol_NavHighlight: return "NavHighlight";
5594 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5595 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5596 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5597 }
5598 IM_ASSERT(0);
5599 return "Unknown";
5600}
5601
5602bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5603{
5604 if (window->RootWindow == potential_parent)
5605 return true;
5606 while (window != NULL)
5607 {
5608 if (window == potential_parent)
5609 return true;
5610 window = window->ParentWindow;
5611 }
5612 return false;
5613}
5614
5615bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5616{
5617 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
5618 ImGuiContext& g = *GImGui;
5619
5620 if (flags & ImGuiHoveredFlags_AnyWindow)
5621 {
5622 if (g.HoveredWindow == NULL)
5623 return false;
5624 }
5625 else
5626 {
5627 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5628 {
5629 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5630 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5631 return false;
5632 break;
5633 case ImGuiHoveredFlags_RootWindow:
5634 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5635 return false;
5636 break;
5637 case ImGuiHoveredFlags_ChildWindows:
5638 if (g.HoveredWindow == NULL || !IsWindowChildOf(window: g.HoveredWindow, potential_parent: g.CurrentWindow))
5639 return false;
5640 break;
5641 default:
5642 if (g.HoveredWindow != g.CurrentWindow)
5643 return false;
5644 break;
5645 }
5646 }
5647
5648 if (!IsWindowContentHoverable(window: g.HoveredRootWindow, flags))
5649 return false;
5650 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5651 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5652 return false;
5653 return true;
5654}
5655
5656bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5657{
5658 ImGuiContext& g = *GImGui;
5659
5660 if (flags & ImGuiFocusedFlags_AnyWindow)
5661 return g.NavWindow != NULL;
5662
5663 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
5664 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5665 {
5666 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5667 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5668 case ImGuiFocusedFlags_RootWindow:
5669 return g.NavWindow == g.CurrentWindow->RootWindow;
5670 case ImGuiFocusedFlags_ChildWindows:
5671 return g.NavWindow && IsWindowChildOf(window: g.NavWindow, potential_parent: g.CurrentWindow);
5672 default:
5673 return g.NavWindow == g.CurrentWindow;
5674 }
5675}
5676
5677// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
5678// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
5679// If you want a window to never be focused, you may use the e.g. NoInputs flag.
5680bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
5681{
5682 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
5683}
5684
5685float ImGui::GetWindowWidth()
5686{
5687 ImGuiWindow* window = GImGui->CurrentWindow;
5688 return window->Size.x;
5689}
5690
5691float ImGui::GetWindowHeight()
5692{
5693 ImGuiWindow* window = GImGui->CurrentWindow;
5694 return window->Size.y;
5695}
5696
5697ImVec2 ImGui::GetWindowPos()
5698{
5699 ImGuiContext& g = *GImGui;
5700 ImGuiWindow* window = g.CurrentWindow;
5701 return window->Pos;
5702}
5703
5704void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
5705{
5706 window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5707 window->Scroll.x = new_scroll_x;
5708 window->DC.CursorMaxPos.x -= window->Scroll.x;
5709}
5710
5711void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5712{
5713 window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5714 window->Scroll.y = new_scroll_y;
5715 window->DC.CursorMaxPos.y -= window->Scroll.y;
5716}
5717
5718static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5719{
5720 // Test condition (NB: bit 0 is always true) and clear flags for next time
5721 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5722 return;
5723
5724 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5725 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5726 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5727
5728 // Set
5729 const ImVec2 old_pos = window->Pos;
5730 window->Pos = ImFloor(v: pos);
5731 window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
5732 window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5733}
5734
5735void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5736{
5737 ImGuiWindow* window = GetCurrentWindowRead();
5738 SetWindowPos(window, pos, cond);
5739}
5740
5741void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5742{
5743 if (ImGuiWindow* window = FindWindowByName(name))
5744 SetWindowPos(window, pos, cond);
5745}
5746
5747ImVec2 ImGui::GetWindowSize()
5748{
5749 ImGuiWindow* window = GetCurrentWindowRead();
5750 return window->Size;
5751}
5752
5753static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5754{
5755 // Test condition (NB: bit 0 is always true) and clear flags for next time
5756 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5757 return;
5758
5759 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5760 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5761
5762 // Set
5763 if (size.x > 0.0f)
5764 {
5765 window->AutoFitFramesX = 0;
5766 window->SizeFull.x = ImFloor(f: size.x);
5767 }
5768 else
5769 {
5770 window->AutoFitFramesX = 2;
5771 window->AutoFitOnlyGrows = false;
5772 }
5773 if (size.y > 0.0f)
5774 {
5775 window->AutoFitFramesY = 0;
5776 window->SizeFull.y = ImFloor(f: size.y);
5777 }
5778 else
5779 {
5780 window->AutoFitFramesY = 2;
5781 window->AutoFitOnlyGrows = false;
5782 }
5783}
5784
5785void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5786{
5787 SetWindowSize(window: GImGui->CurrentWindow, size, cond);
5788}
5789
5790void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5791{
5792 if (ImGuiWindow* window = FindWindowByName(name))
5793 SetWindowSize(window, size, cond);
5794}
5795
5796static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
5797{
5798 // Test condition (NB: bit 0 is always true) and clear flags for next time
5799 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
5800 return;
5801 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5802
5803 // Set
5804 window->Collapsed = collapsed;
5805}
5806
5807void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
5808{
5809 SetWindowCollapsed(window: GImGui->CurrentWindow, collapsed, cond);
5810}
5811
5812bool ImGui::IsWindowCollapsed()
5813{
5814 ImGuiWindow* window = GetCurrentWindowRead();
5815 return window->Collapsed;
5816}
5817
5818bool ImGui::IsWindowAppearing()
5819{
5820 ImGuiWindow* window = GetCurrentWindowRead();
5821 return window->Appearing;
5822}
5823
5824void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
5825{
5826 if (ImGuiWindow* window = FindWindowByName(name))
5827 SetWindowCollapsed(window, collapsed, cond);
5828}
5829
5830void ImGui::SetWindowFocus()
5831{
5832 FocusWindow(window: GImGui->CurrentWindow);
5833}
5834
5835void ImGui::SetWindowFocus(const char* name)
5836{
5837 if (name)
5838 {
5839 if (ImGuiWindow* window = FindWindowByName(name))
5840 FocusWindow(window);
5841 }
5842 else
5843 {
5844 FocusWindow(NULL);
5845 }
5846}
5847
5848void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
5849{
5850 ImGuiContext& g = *GImGui;
5851 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5852 g.NextWindowData.PosVal = pos;
5853 g.NextWindowData.PosPivotVal = pivot;
5854 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
5855}
5856
5857void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
5858{
5859 ImGuiContext& g = *GImGui;
5860 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5861 g.NextWindowData.SizeVal = size;
5862 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
5863}
5864
5865void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
5866{
5867 ImGuiContext& g = *GImGui;
5868 g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
5869 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
5870 g.NextWindowData.SizeCallback = custom_callback;
5871 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
5872}
5873
5874void ImGui::SetNextWindowContentSize(const ImVec2& size)
5875{
5876 ImGuiContext& g = *GImGui;
5877 g.NextWindowData.ContentSizeVal = size; // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value.
5878 g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
5879}
5880
5881void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
5882{
5883 ImGuiContext& g = *GImGui;
5884 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5885 g.NextWindowData.CollapsedVal = collapsed;
5886 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
5887}
5888
5889void ImGui::SetNextWindowFocus()
5890{
5891 ImGuiContext& g = *GImGui;
5892 g.NextWindowData.FocusCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5893}
5894
5895void ImGui::SetNextWindowBgAlpha(float alpha)
5896{
5897 ImGuiContext& g = *GImGui;
5898 g.NextWindowData.BgAlphaVal = alpha;
5899 g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5900}
5901
5902// In window space (not screen space!)
5903ImVec2 ImGui::GetContentRegionMax()
5904{
5905 ImGuiWindow* window = GetCurrentWindowRead();
5906 ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
5907 if (window->DC.ColumnsSet)
5908 mx.x = GetColumnOffset(column_index: window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
5909 return mx;
5910}
5911
5912ImVec2 ImGui::GetContentRegionAvail()
5913{
5914 ImGuiWindow* window = GetCurrentWindowRead();
5915 return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5916}
5917
5918float ImGui::GetContentRegionAvailWidth()
5919{
5920 return GetContentRegionAvail().x;
5921}
5922
5923// In window space (not screen space!)
5924ImVec2 ImGui::GetWindowContentRegionMin()
5925{
5926 ImGuiWindow* window = GetCurrentWindowRead();
5927 return window->ContentsRegionRect.Min - window->Pos;
5928}
5929
5930ImVec2 ImGui::GetWindowContentRegionMax()
5931{
5932 ImGuiWindow* window = GetCurrentWindowRead();
5933 return window->ContentsRegionRect.Max - window->Pos;
5934}
5935
5936float ImGui::GetWindowContentRegionWidth()
5937{
5938 ImGuiWindow* window = GetCurrentWindowRead();
5939 return window->ContentsRegionRect.GetWidth();
5940}
5941
5942float ImGui::GetTextLineHeight()
5943{
5944 ImGuiContext& g = *GImGui;
5945 return g.FontSize;
5946}
5947
5948float ImGui::GetTextLineHeightWithSpacing()
5949{
5950 ImGuiContext& g = *GImGui;
5951 return g.FontSize + g.Style.ItemSpacing.y;
5952}
5953
5954float ImGui::GetFrameHeight()
5955{
5956 ImGuiContext& g = *GImGui;
5957 return g.FontSize + g.Style.FramePadding.y * 2.0f;
5958}
5959
5960float ImGui::GetFrameHeightWithSpacing()
5961{
5962 ImGuiContext& g = *GImGui;
5963 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5964}
5965
5966ImDrawList* ImGui::GetWindowDrawList()
5967{
5968 ImGuiWindow* window = GetCurrentWindow();
5969 return window->DrawList;
5970}
5971
5972ImFont* ImGui::GetFont()
5973{
5974 return GImGui->Font;
5975}
5976
5977float ImGui::GetFontSize()
5978{
5979 return GImGui->FontSize;
5980}
5981
5982ImVec2 ImGui::GetFontTexUvWhitePixel()
5983{
5984 return GImGui->DrawListSharedData.TexUvWhitePixel;
5985}
5986
5987void ImGui::SetWindowFontScale(float scale)
5988{
5989 ImGuiContext& g = *GImGui;
5990 ImGuiWindow* window = GetCurrentWindow();
5991 window->FontWindowScale = scale;
5992 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
5993}
5994
5995// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5996// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
5997ImVec2 ImGui::GetCursorPos()
5998{
5999 ImGuiWindow* window = GetCurrentWindowRead();
6000 return window->DC.CursorPos - window->Pos + window->Scroll;
6001}
6002
6003float ImGui::GetCursorPosX()
6004{
6005 ImGuiWindow* window = GetCurrentWindowRead();
6006 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6007}
6008
6009float ImGui::GetCursorPosY()
6010{
6011 ImGuiWindow* window = GetCurrentWindowRead();
6012 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6013}
6014
6015void ImGui::SetCursorPos(const ImVec2& local_pos)
6016{
6017 ImGuiWindow* window = GetCurrentWindow();
6018 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6019 window->DC.CursorMaxPos = ImMax(lhs: window->DC.CursorMaxPos, rhs: window->DC.CursorPos);
6020}
6021
6022void ImGui::SetCursorPosX(float x)
6023{
6024 ImGuiWindow* window = GetCurrentWindow();
6025 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6026 window->DC.CursorMaxPos.x = ImMax(lhs: window->DC.CursorMaxPos.x, rhs: window->DC.CursorPos.x);
6027}
6028
6029void ImGui::SetCursorPosY(float y)
6030{
6031 ImGuiWindow* window = GetCurrentWindow();
6032 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6033 window->DC.CursorMaxPos.y = ImMax(lhs: window->DC.CursorMaxPos.y, rhs: window->DC.CursorPos.y);
6034}
6035
6036ImVec2 ImGui::GetCursorStartPos()
6037{
6038 ImGuiWindow* window = GetCurrentWindowRead();
6039 return window->DC.CursorStartPos - window->Pos;
6040}
6041
6042ImVec2 ImGui::GetCursorScreenPos()
6043{
6044 ImGuiWindow* window = GetCurrentWindowRead();
6045 return window->DC.CursorPos;
6046}
6047
6048void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
6049{
6050 ImGuiWindow* window = GetCurrentWindow();
6051 window->DC.CursorPos = screen_pos;
6052 window->DC.CursorMaxPos = ImMax(lhs: window->DC.CursorMaxPos, rhs: window->DC.CursorPos);
6053}
6054
6055float ImGui::GetScrollX()
6056{
6057 return GImGui->CurrentWindow->Scroll.x;
6058}
6059
6060float ImGui::GetScrollY()
6061{
6062 return GImGui->CurrentWindow->Scroll.y;
6063}
6064
6065float ImGui::GetScrollMaxX()
6066{
6067 return GetWindowScrollMaxX(window: GImGui->CurrentWindow);
6068}
6069
6070float ImGui::GetScrollMaxY()
6071{
6072 return GetWindowScrollMaxY(window: GImGui->CurrentWindow);
6073}
6074
6075void ImGui::SetScrollX(float scroll_x)
6076{
6077 ImGuiWindow* window = GetCurrentWindow();
6078 window->ScrollTarget.x = scroll_x;
6079 window->ScrollTargetCenterRatio.x = 0.0f;
6080}
6081
6082void ImGui::SetScrollY(float scroll_y)
6083{
6084 ImGuiWindow* window = GetCurrentWindow();
6085 window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
6086 window->ScrollTargetCenterRatio.y = 0.0f;
6087}
6088
6089void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
6090{
6091 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
6092 ImGuiWindow* window = GetCurrentWindow();
6093 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
6094 window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
6095 window->ScrollTargetCenterRatio.y = center_y_ratio;
6096}
6097
6098// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
6099void ImGui::SetScrollHereY(float center_y_ratio)
6100{
6101 ImGuiWindow* window = GetCurrentWindow();
6102 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
6103 target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
6104 SetScrollFromPosY(pos_y: target_y, center_y_ratio);
6105}
6106
6107void ImGui::ActivateItem(ImGuiID id)
6108{
6109 ImGuiContext& g = *GImGui;
6110 g.NavNextActivateId = id;
6111}
6112
6113void ImGui::SetKeyboardFocusHere(int offset)
6114{
6115 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6116 ImGuiWindow* window = GetCurrentWindow();
6117 window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6118 window->FocusIdxTabRequestNext = INT_MAX;
6119}
6120
6121void ImGui::SetItemDefaultFocus()
6122{
6123 ImGuiContext& g = *GImGui;
6124 ImGuiWindow* window = g.CurrentWindow;
6125 if (!window->Appearing)
6126 return;
6127 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6128 {
6129 g.NavInitRequest = false;
6130 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6131 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6132 NavUpdateAnyRequestFlag();
6133 if (!IsItemVisible())
6134 SetScrollHereY();
6135 }
6136}
6137
6138void ImGui::SetStateStorage(ImGuiStorage* tree)
6139{
6140 ImGuiWindow* window = GetCurrentWindow();
6141 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6142}
6143
6144ImGuiStorage* ImGui::GetStateStorage()
6145{
6146 ImGuiWindow* window = GetCurrentWindowRead();
6147 return window->DC.StateStorage;
6148}
6149
6150void ImGui::PushID(const char* str_id)
6151{
6152 ImGuiWindow* window = GetCurrentWindowRead();
6153 window->IDStack.push_back(v: window->GetIDNoKeepAlive(str: str_id));
6154}
6155
6156void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6157{
6158 ImGuiWindow* window = GetCurrentWindowRead();
6159 window->IDStack.push_back(v: window->GetIDNoKeepAlive(str: str_id_begin, str_end: str_id_end));
6160}
6161
6162void ImGui::PushID(const void* ptr_id)
6163{
6164 ImGuiWindow* window = GetCurrentWindowRead();
6165 window->IDStack.push_back(v: window->GetIDNoKeepAlive(ptr: ptr_id));
6166}
6167
6168void ImGui::PushID(int int_id)
6169{
6170 const void* ptr_id = (void*)(intptr_t)int_id;
6171 ImGuiWindow* window = GetCurrentWindowRead();
6172 window->IDStack.push_back(v: window->GetIDNoKeepAlive(ptr: ptr_id));
6173}
6174
6175void ImGui::PopID()
6176{
6177 ImGuiWindow* window = GetCurrentWindowRead();
6178 window->IDStack.pop_back();
6179}
6180
6181ImGuiID ImGui::GetID(const char* str_id)
6182{
6183 return GImGui->CurrentWindow->GetID(str: str_id);
6184}
6185
6186ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6187{
6188 return GImGui->CurrentWindow->GetID(str: str_id_begin, str_end: str_id_end);
6189}
6190
6191ImGuiID ImGui::GetID(const void* ptr_id)
6192{
6193 return GImGui->CurrentWindow->GetID(ptr: ptr_id);
6194}
6195
6196bool ImGui::IsRectVisible(const ImVec2& size)
6197{
6198 ImGuiWindow* window = GetCurrentWindowRead();
6199 return window->ClipRect.Overlaps(r: ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6200}
6201
6202bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6203{
6204 ImGuiWindow* window = GetCurrentWindowRead();
6205 return window->ClipRect.Overlaps(r: ImRect(rect_min, rect_max));
6206}
6207
6208// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
6209void ImGui::BeginGroup()
6210{
6211 ImGuiContext& g = *GImGui;
6212 ImGuiWindow* window = GetCurrentWindow();
6213
6214 window->DC.GroupStack.resize(new_size: window->DC.GroupStack.Size + 1);
6215 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6216 group_data.BackupCursorPos = window->DC.CursorPos;
6217 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6218 group_data.BackupIndent = window->DC.Indent;
6219 group_data.BackupGroupOffset = window->DC.GroupOffset;
6220 group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6221 group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6222 group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6223 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6224 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6225 group_data.AdvanceCursor = true;
6226
6227 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6228 window->DC.Indent = window->DC.GroupOffset;
6229 window->DC.CursorMaxPos = window->DC.CursorPos;
6230 window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6231 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6232}
6233
6234void ImGui::EndGroup()
6235{
6236 ImGuiContext& g = *GImGui;
6237 ImGuiWindow* window = GetCurrentWindow();
6238 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
6239
6240 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6241
6242 ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6243 group_bb.Max = ImMax(lhs: group_bb.Min, rhs: group_bb.Max);
6244
6245 window->DC.CursorPos = group_data.BackupCursorPos;
6246 window->DC.CursorMaxPos = ImMax(lhs: group_data.BackupCursorMaxPos, rhs: window->DC.CursorMaxPos);
6247 window->DC.Indent = group_data.BackupIndent;
6248 window->DC.GroupOffset = group_data.BackupGroupOffset;
6249 window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6250 window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6251 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6252
6253 if (group_data.AdvanceCursor)
6254 {
6255 window->DC.CurrentLineTextBaseOffset = ImMax(lhs: window->DC.PrevLineTextBaseOffset, rhs: group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
6256 ItemSize(size: group_bb.GetSize(), text_offset_y: group_data.BackupCurrentLineTextBaseOffset);
6257 ItemAdd(bb: group_bb, id: 0);
6258 }
6259
6260 // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
6261 // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
6262 // (and if you grep for LastItemId you'll notice it is only used in that context.
6263 if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6264 window->DC.LastItemId = g.ActiveId;
6265 else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6266 window->DC.LastItemId = g.ActiveIdPreviousFrame;
6267 window->DC.LastItemRect = group_bb;
6268
6269 window->DC.GroupStack.pop_back();
6270
6271 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
6272}
6273
6274// Gets back to previous line and continue with horizontal layout
6275// pos_x == 0 : follow right after previous item
6276// pos_x != 0 : align to specified x position (relative to window/group left)
6277// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
6278// spacing_w >= 0 : enforce spacing amount
6279void ImGui::SameLine(float pos_x, float spacing_w)
6280{
6281 ImGuiWindow* window = GetCurrentWindow();
6282 if (window->SkipItems)
6283 return;
6284
6285 ImGuiContext& g = *GImGui;
6286 if (pos_x != 0.0f)
6287 {
6288 if (spacing_w < 0.0f) spacing_w = 0.0f;
6289 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6290 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6291 }
6292 else
6293 {
6294 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6295 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6296 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6297 }
6298 window->DC.CurrentLineSize = window->DC.PrevLineSize;
6299 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6300}
6301
6302void ImGui::Indent(float indent_w)
6303{
6304 ImGuiContext& g = *GImGui;
6305 ImGuiWindow* window = GetCurrentWindow();
6306 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6307 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6308}
6309
6310void ImGui::Unindent(float indent_w)
6311{
6312 ImGuiContext& g = *GImGui;
6313 ImGuiWindow* window = GetCurrentWindow();
6314 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6315 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6316}
6317
6318//-----------------------------------------------------------------------------
6319// [SECTION] TOOLTIPS
6320//-----------------------------------------------------------------------------
6321
6322void ImGui::BeginTooltip()
6323{
6324 ImGuiContext& g = *GImGui;
6325 if (g.DragDropWithinSourceOrTarget)
6326 {
6327 // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor)
6328 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6329 // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do.
6330 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6331 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6332 SetNextWindowPos(pos: tooltip_pos);
6333 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6334 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6335 BeginTooltipEx(extra_flags: 0, override_previous_tooltip: true);
6336 }
6337 else
6338 {
6339 BeginTooltipEx(extra_flags: 0, override_previous_tooltip: false);
6340 }
6341}
6342
6343// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
6344void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6345{
6346 ImGuiContext& g = *GImGui;
6347 char window_name[16];
6348 ImFormatString(buf: window_name, IM_ARRAYSIZE(window_name), fmt: "##Tooltip_%02d", g.TooltipOverrideCount);
6349 if (override_previous_tooltip)
6350 if (ImGuiWindow* window = FindWindowByName(name: window_name))
6351 if (window->Active)
6352 {
6353 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6354 window->Hidden = true;
6355 window->HiddenFramesRegular = 1;
6356 ImFormatString(buf: window_name, IM_ARRAYSIZE(window_name), fmt: "##Tooltip_%02d", ++g.TooltipOverrideCount);
6357 }
6358 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
6359 Begin(name: window_name, NULL, flags: flags | extra_flags);
6360}
6361
6362void ImGui::EndTooltip()
6363{
6364 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
6365 End();
6366}
6367
6368void ImGui::SetTooltipV(const char* fmt, va_list args)
6369{
6370 ImGuiContext& g = *GImGui;
6371 if (g.DragDropWithinSourceOrTarget)
6372 BeginTooltip();
6373 else
6374 BeginTooltipEx(extra_flags: 0, override_previous_tooltip: true);
6375 TextV(fmt, args);
6376 EndTooltip();
6377}
6378
6379void ImGui::SetTooltip(const char* fmt, ...)
6380{
6381 va_list args;
6382 va_start(args, fmt);
6383 SetTooltipV(fmt, args);
6384 va_end(args);
6385}
6386
6387//-----------------------------------------------------------------------------
6388// [SECTION] POPUPS
6389//-----------------------------------------------------------------------------
6390
6391bool ImGui::IsPopupOpen(ImGuiID id)
6392{
6393 ImGuiContext& g = *GImGui;
6394 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
6395}
6396
6397bool ImGui::IsPopupOpen(const char* str_id)
6398{
6399 ImGuiContext& g = *GImGui;
6400 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str: str_id);
6401}
6402
6403ImGuiWindow* ImGui::GetFrontMostPopupModal()
6404{
6405 ImGuiContext& g = *GImGui;
6406 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6407 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6408 if (popup->Flags & ImGuiWindowFlags_Modal)
6409 return popup;
6410 return NULL;
6411}
6412
6413void ImGui::OpenPopup(const char* str_id)
6414{
6415 ImGuiContext& g = *GImGui;
6416 OpenPopupEx(id: g.CurrentWindow->GetID(str: str_id));
6417}
6418
6419// Mark popup as open (toggle toward open state).
6420// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6421// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6422// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
6423void ImGui::OpenPopupEx(ImGuiID id)
6424{
6425 ImGuiContext& g = *GImGui;
6426 ImGuiWindow* parent_window = g.CurrentWindow;
6427 int current_stack_size = g.CurrentPopupStack.Size;
6428 ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6429 popup_ref.PopupId = id;
6430 popup_ref.Window = NULL;
6431 popup_ref.ParentWindow = parent_window;
6432 popup_ref.OpenFrameCount = g.FrameCount;
6433 popup_ref.OpenParentId = parent_window->IDStack.back();
6434 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6435 popup_ref.OpenMousePos = IsMousePosValid(mouse_pos: &g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
6436
6437 //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6438 if (g.OpenPopupStack.Size < current_stack_size + 1)
6439 {
6440 g.OpenPopupStack.push_back(v: popup_ref);
6441 }
6442 else
6443 {
6444 // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui
6445 // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing
6446 // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand.
6447 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6448 {
6449 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6450 }
6451 else
6452 {
6453 // Close child popups if any, then flag popup for open/reopen
6454 g.OpenPopupStack.resize(new_size: current_stack_size + 1);
6455 g.OpenPopupStack[current_stack_size] = popup_ref;
6456 }
6457
6458 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6459 // This is equivalent to what ClosePopupToLevel() does.
6460 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
6461 // FocusWindow(parent_window);
6462 }
6463}
6464
6465bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6466{
6467 ImGuiWindow* window = GImGui->CurrentWindow;
6468 if (IsMouseReleased(button: mouse_button) && IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6469 {
6470 ImGuiID id = str_id ? window->GetID(str: str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
6471 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6472 OpenPopupEx(id);
6473 return true;
6474 }
6475 return false;
6476}
6477
6478void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6479{
6480 ImGuiContext& g = *GImGui;
6481 if (g.OpenPopupStack.empty())
6482 return;
6483
6484 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6485 // Don't close our own child popup windows.
6486 int n = 0;
6487 if (ref_window)
6488 {
6489 for (n = 0; n < g.OpenPopupStack.Size; n++)
6490 {
6491 ImGuiPopupRef& popup = g.OpenPopupStack[n];
6492 if (!popup.Window)
6493 continue;
6494 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6495 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6496 continue;
6497
6498 // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6499 bool has_focus = false;
6500 for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
6501 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
6502 if (!has_focus)
6503 break;
6504 }
6505 }
6506 if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
6507 ClosePopupToLevel(remaining: n);
6508}
6509
6510void ImGui::ClosePopupToLevel(int remaining)
6511{
6512 IM_ASSERT(remaining >= 0);
6513 ImGuiContext& g = *GImGui;
6514 ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6515 if (g.NavLayer == 0)
6516 focus_window = NavRestoreLastChildNavWindow(window: focus_window);
6517 FocusWindow(window: focus_window);
6518 focus_window->DC.NavHideHighlightOneFrame = true;
6519 g.OpenPopupStack.resize(new_size: remaining);
6520}
6521
6522void ImGui::ClosePopup(ImGuiID id)
6523{
6524 if (!IsPopupOpen(id))
6525 return;
6526 ImGuiContext& g = *GImGui;
6527 ClosePopupToLevel(remaining: g.OpenPopupStack.Size - 1);
6528}
6529
6530// Close the popup we have begin-ed into.
6531void ImGui::CloseCurrentPopup()
6532{
6533 ImGuiContext& g = *GImGui;
6534 int popup_idx = g.CurrentPopupStack.Size - 1;
6535 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6536 return;
6537 while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
6538 popup_idx--;
6539 ClosePopupToLevel(remaining: popup_idx);
6540}
6541
6542bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6543{
6544 ImGuiContext& g = *GImGui;
6545 if (!IsPopupOpen(id))
6546 {
6547 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6548 return false;
6549 }
6550
6551 char name[20];
6552 if (extra_flags & ImGuiWindowFlags_ChildMenu)
6553 ImFormatString(buf: name, IM_ARRAYSIZE(name), fmt: "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
6554 else
6555 ImFormatString(buf: name, IM_ARRAYSIZE(name), fmt: "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6556
6557 bool is_open = Begin(name, NULL, flags: extra_flags | ImGuiWindowFlags_Popup);
6558 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6559 EndPopup();
6560
6561 return is_open;
6562}
6563
6564bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6565{
6566 ImGuiContext& g = *GImGui;
6567 if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
6568 {
6569 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6570 return false;
6571 }
6572 return BeginPopupEx(id: g.CurrentWindow->GetID(str: str_id), extra_flags: flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6573}
6574
6575bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6576{
6577 ImGuiContext& g = *GImGui;
6578 ImGuiWindow* window = g.CurrentWindow;
6579 const ImGuiID id = window->GetID(str: name);
6580 if (!IsPopupOpen(id))
6581 {
6582 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6583 return false;
6584 }
6585
6586 // Center modal windows by default
6587 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6588 if (g.NextWindowData.PosCond == 0)
6589 SetNextWindowPos(pos: g.IO.DisplaySize * 0.5f, cond: ImGuiCond_Appearing, pivot: ImVec2(0.5f, 0.5f));
6590
6591 bool is_open = Begin(name, p_open, flags: flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
6592 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6593 {
6594 EndPopup();
6595 if (is_open)
6596 ClosePopup(id);
6597 return false;
6598 }
6599 return is_open;
6600}
6601
6602void ImGui::EndPopup()
6603{
6604 ImGuiContext& g = *GImGui; (void)g;
6605 IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
6606 IM_ASSERT(g.CurrentPopupStack.Size > 0);
6607
6608 // Make all menus and popups wrap around for now, may need to expose that policy.
6609 NavMoveRequestTryWrapping(window: g.CurrentWindow, move_flags: ImGuiNavMoveFlags_LoopY);
6610
6611 End();
6612}
6613
6614// This is a helper to handle the simplest case of associating one named popup to one given widget.
6615// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6616// You can pass a NULL str_id to use the identifier of the last item.
6617bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6618{
6619 ImGuiWindow* window = GImGui->CurrentWindow;
6620 ImGuiID id = str_id ? window->GetID(str: str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
6621 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6622 if (IsMouseReleased(button: mouse_button) && IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6623 OpenPopupEx(id);
6624 return BeginPopupEx(id, extra_flags: ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6625}
6626
6627bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6628{
6629 if (!str_id)
6630 str_id = "window_context";
6631 ImGuiID id = GImGui->CurrentWindow->GetID(str: str_id);
6632 if (IsMouseReleased(button: mouse_button) && IsWindowHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6633 if (also_over_items || !IsAnyItemHovered())
6634 OpenPopupEx(id);
6635 return BeginPopupEx(id, extra_flags: ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6636}
6637
6638bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
6639{
6640 if (!str_id)
6641 str_id = "void_context";
6642 ImGuiID id = GImGui->CurrentWindow->GetID(str: str_id);
6643 if (IsMouseReleased(button: mouse_button) && !IsWindowHovered(flags: ImGuiHoveredFlags_AnyWindow))
6644 OpenPopupEx(id);
6645 return BeginPopupEx(id, extra_flags: ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6646}
6647
6648ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
6649{
6650 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
6651 ImRect r_screen = GetViewportRect();
6652 r_screen.Expand(amount: ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
6653 return r_screen;
6654}
6655
6656// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
6657// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
6658ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
6659{
6660 ImVec2 base_pos_clamped = ImClamp(v: ref_pos, mn: r_outer.Min, mx: r_outer.Max - size);
6661 //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
6662 //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
6663
6664 // Combo Box policy (we want a connecting edge)
6665 if (policy == ImGuiPopupPositionPolicy_ComboBox)
6666 {
6667 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
6668 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6669 {
6670 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6671 if (n != -1 && dir == *last_dir) // Already tried this direction?
6672 continue;
6673 ImVec2 pos;
6674 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
6675 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
6676 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
6677 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
6678 if (!r_outer.Contains(r: ImRect(pos, pos + size)))
6679 continue;
6680 *last_dir = dir;
6681 return pos;
6682 }
6683 }
6684
6685 // Default popup policy
6686 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
6687 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6688 {
6689 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6690 if (n != -1 && dir == *last_dir) // Already tried this direction?
6691 continue;
6692 float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
6693 float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
6694 if (avail_w < size.x || avail_h < size.y)
6695 continue;
6696 ImVec2 pos;
6697 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
6698 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
6699 *last_dir = dir;
6700 return pos;
6701 }
6702
6703 // Fallback, try to keep within display
6704 *last_dir = ImGuiDir_None;
6705 ImVec2 pos = ref_pos;
6706 pos.x = ImMax(lhs: ImMin(lhs: pos.x + size.x, rhs: r_outer.Max.x) - size.x, rhs: r_outer.Min.x);
6707 pos.y = ImMax(lhs: ImMin(lhs: pos.y + size.y, rhs: r_outer.Max.y) - size.y, rhs: r_outer.Min.y);
6708 return pos;
6709}
6710
6711ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
6712{
6713 ImGuiContext& g = *GImGui;
6714
6715 ImRect r_outer = GetWindowAllowedExtentRect(window);
6716 if (window->Flags & ImGuiWindowFlags_ChildMenu)
6717 {
6718 // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds.
6719 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
6720 IM_ASSERT(g.CurrentWindow == window);
6721 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
6722 float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
6723 ImRect r_avoid;
6724 if (parent_window->DC.MenuBarAppending)
6725 r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
6726 else
6727 r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
6728 return FindBestWindowPosForPopupEx(ref_pos: window->Pos, size: window->Size, last_dir: &window->AutoPosLastDirection, r_outer, r_avoid);
6729 }
6730 if (window->Flags & ImGuiWindowFlags_Popup)
6731 {
6732 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
6733 return FindBestWindowPosForPopupEx(ref_pos: window->Pos, size: window->Size, last_dir: &window->AutoPosLastDirection, r_outer, r_avoid);
6734 }
6735 if (window->Flags & ImGuiWindowFlags_Tooltip)
6736 {
6737 // Position tooltip (always follows mouse)
6738 float sc = g.Style.MouseCursorScale;
6739 ImVec2 ref_pos = NavCalcPreferredRefPos();
6740 ImRect r_avoid;
6741 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
6742 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
6743 else
6744 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
6745 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, size: window->Size, last_dir: &window->AutoPosLastDirection, r_outer, r_avoid);
6746 if (window->AutoPosLastDirection == ImGuiDir_None)
6747 pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
6748 return pos;
6749 }
6750 IM_ASSERT(0);
6751 return window->Pos;
6752}
6753
6754//-----------------------------------------------------------------------------
6755// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
6756//-----------------------------------------------------------------------------
6757
6758ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
6759{
6760 if (ImFabs(x: dx) > ImFabs(x: dy))
6761 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
6762 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
6763}
6764
6765static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
6766{
6767 if (a1 < b0)
6768 return a1 - b0;
6769 if (b1 < a0)
6770 return a0 - b1;
6771 return 0.0f;
6772}
6773
6774static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
6775{
6776 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
6777 {
6778 r.Min.y = ImClamp(v: r.Min.y, mn: clip_rect.Min.y, mx: clip_rect.Max.y);
6779 r.Max.y = ImClamp(v: r.Max.y, mn: clip_rect.Min.y, mx: clip_rect.Max.y);
6780 }
6781 else
6782 {
6783 r.Min.x = ImClamp(v: r.Min.x, mn: clip_rect.Min.x, mx: clip_rect.Max.x);
6784 r.Max.x = ImClamp(v: r.Max.x, mn: clip_rect.Min.x, mx: clip_rect.Max.x);
6785 }
6786}
6787
6788// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
6789static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
6790{
6791 ImGuiContext& g = *GImGui;
6792 ImGuiWindow* window = g.CurrentWindow;
6793 if (g.NavLayer != window->DC.NavLayerCurrent)
6794 return false;
6795
6796 const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
6797 g.NavScoringCount++;
6798
6799 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
6800 if (window->ParentWindow == g.NavWindow)
6801 {
6802 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
6803 if (!window->ClipRect.Contains(r: cand))
6804 return false;
6805 cand.ClipWithFull(r: window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
6806 }
6807
6808 // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
6809 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
6810 NavClampRectToVisibleAreaForMoveDir(move_dir: g.NavMoveClipDir, r&: cand, clip_rect: window->ClipRect);
6811
6812 // Compute distance between boxes
6813 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
6814 float dbx = NavScoreItemDistInterval(a0: cand.Min.x, a1: cand.Max.x, b0: curr.Min.x, b1: curr.Max.x);
6815 float dby = NavScoreItemDistInterval(a0: ImLerp(a: cand.Min.y, b: cand.Max.y, t: 0.2f), a1: ImLerp(a: cand.Min.y, b: cand.Max.y, t: 0.8f), b0: ImLerp(a: curr.Min.y, b: curr.Max.y, t: 0.2f), b1: ImLerp(a: curr.Min.y, b: curr.Max.y, t: 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
6816 if (dby != 0.0f && dbx != 0.0f)
6817 dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
6818 float dist_box = ImFabs(x: dbx) + ImFabs(x: dby);
6819
6820 // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
6821 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
6822 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
6823 float dist_center = ImFabs(x: dcx) + ImFabs(x: dcy); // L1 metric (need this for our connectedness guarantee)
6824
6825 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
6826 ImGuiDir quadrant;
6827 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
6828 if (dbx != 0.0f || dby != 0.0f)
6829 {
6830 // For non-overlapping boxes, use distance between boxes
6831 dax = dbx;
6832 day = dby;
6833 dist_axial = dist_box;
6834 quadrant = ImGetDirQuadrantFromDelta(dx: dbx, dy: dby);
6835 }
6836 else if (dcx != 0.0f || dcy != 0.0f)
6837 {
6838 // For overlapping boxes with different centers, use distance between centers
6839 dax = dcx;
6840 day = dcy;
6841 dist_axial = dist_center;
6842 quadrant = ImGetDirQuadrantFromDelta(dx: dcx, dy: dcy);
6843 }
6844 else
6845 {
6846 // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)
6847 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
6848 }
6849
6850#if IMGUI_DEBUG_NAV_SCORING
6851 char buf[128];
6852 if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
6853 {
6854 ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]);
6855 ImDrawList* draw_list = GetOverlayDrawList(window);
6856 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
6857 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
6858 draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
6859 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
6860 }
6861 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
6862 {
6863 if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
6864 if (quadrant == g.NavMoveDir)
6865 {
6866 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
6867 ImDrawList* draw_list = GetOverlayDrawList(window);
6868 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
6869 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
6870 }
6871 }
6872 #endif
6873
6874 // Is it in the quadrant we're interesting in moving to?
6875 bool new_best = false;
6876 if (quadrant == g.NavMoveDir)
6877 {
6878 // Does it beat the current best candidate?
6879 if (dist_box < result->DistBox)
6880 {
6881 result->DistBox = dist_box;
6882 result->DistCenter = dist_center;
6883 return true;
6884 }
6885 if (dist_box == result->DistBox)
6886 {
6887 // Try using distance between center points to break ties
6888 if (dist_center < result->DistCenter)
6889 {
6890 result->DistCenter = dist_center;
6891 new_best = true;
6892 }
6893 else if (dist_center == result->DistCenter)
6894 {
6895 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
6896 // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
6897 // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
6898 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
6899 new_best = true;
6900 }
6901 }
6902 }
6903
6904 // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches
6905 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
6906 // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
6907 // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
6908 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
6909 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
6910 if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
6911 if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
6912 {
6913 result->DistAxial = dist_axial;
6914 new_best = true;
6915 }
6916
6917 return new_best;
6918}
6919
6920// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
6921static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
6922{
6923 ImGuiContext& g = *GImGui;
6924 //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
6925 // return;
6926
6927 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
6928 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
6929
6930 // Process Init Request
6931 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
6932 {
6933 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
6934 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
6935 {
6936 g.NavInitResultId = id;
6937 g.NavInitResultRectRel = nav_bb_rel;
6938 }
6939 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
6940 {
6941 g.NavInitRequest = false; // Found a match, clear request
6942 NavUpdateAnyRequestFlag();
6943 }
6944 }
6945
6946 // Process Move Request (scoring for navigation)
6947 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
6948 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
6949 {
6950 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
6951#if IMGUI_DEBUG_NAV_SCORING
6952 // [DEBUG] Score all items in NavWindow at all times
6953 if (!g.NavMoveRequest)
6954 g.NavMoveDir = g.NavMoveDirLast;
6955 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
6956#else
6957 bool new_best = g.NavMoveRequest && NavScoreItem(result, cand: nav_bb);
6958#endif
6959 if (new_best)
6960 {
6961 result->ID = id;
6962 result->Window = window;
6963 result->RectRel = nav_bb_rel;
6964 }
6965
6966 const float VISIBLE_RATIO = 0.70f;
6967 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(r: nav_bb))
6968 if (ImClamp(v: nav_bb.Max.y, mn: window->ClipRect.Min.y, mx: window->ClipRect.Max.y) - ImClamp(v: nav_bb.Min.y, mn: window->ClipRect.Min.y, mx: window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
6969 if (NavScoreItem(result: &g.NavMoveResultLocalVisibleSet, cand: nav_bb))
6970 {
6971 result = &g.NavMoveResultLocalVisibleSet;
6972 result->ID = id;
6973 result->Window = window;
6974 result->RectRel = nav_bb_rel;
6975 }
6976 }
6977
6978 // Update window-relative bounding box of navigated item
6979 if (g.NavId == id)
6980 {
6981 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
6982 g.NavLayer = window->DC.NavLayerCurrent;
6983 g.NavIdIsAlive = true;
6984 g.NavIdTabCounter = window->FocusIdxTabCounter;
6985 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
6986 }
6987}
6988
6989bool ImGui::NavMoveRequestButNoResultYet()
6990{
6991 ImGuiContext& g = *GImGui;
6992 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
6993}
6994
6995void ImGui::NavMoveRequestCancel()
6996{
6997 ImGuiContext& g = *GImGui;
6998 g.NavMoveRequest = false;
6999 NavUpdateAnyRequestFlag();
7000}
7001
7002void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7003{
7004 ImGuiContext& g = *GImGui;
7005 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7006 ImGui::NavMoveRequestCancel();
7007 g.NavMoveDir = move_dir;
7008 g.NavMoveClipDir = clip_dir;
7009 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7010 g.NavMoveRequestFlags = move_flags;
7011 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7012}
7013
7014void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7015{
7016 ImGuiContext& g = *GImGui;
7017 if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7018 return;
7019 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7020 ImRect bb_rel = window->NavRectRel[0];
7021
7022 ImGuiDir clip_dir = g.NavMoveDir;
7023 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7024 {
7025 bb_rel.Min.x = bb_rel.Max.x = ImMax(lhs: window->SizeFull.x, rhs: window->SizeContents.x) - window->Scroll.x;
7026 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(dy: -bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7027 NavMoveRequestForward(move_dir: g.NavMoveDir, clip_dir, bb_rel, move_flags);
7028 }
7029 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7030 {
7031 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7032 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(dy: +bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7033 NavMoveRequestForward(move_dir: g.NavMoveDir, clip_dir, bb_rel, move_flags);
7034 }
7035 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7036 {
7037 bb_rel.Min.y = bb_rel.Max.y = ImMax(lhs: window->SizeFull.y, rhs: window->SizeContents.y) - window->Scroll.y;
7038 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(dx: -bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7039 NavMoveRequestForward(move_dir: g.NavMoveDir, clip_dir, bb_rel, move_flags);
7040 }
7041 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7042 {
7043 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7044 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(dx: +bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7045 NavMoveRequestForward(move_dir: g.NavMoveDir, clip_dir, bb_rel, move_flags);
7046 }
7047}
7048
7049static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
7050{
7051 ImGuiWindow* parent_window = nav_window;
7052 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7053 parent_window = parent_window->ParentWindow;
7054 if (parent_window && parent_window != nav_window)
7055 parent_window->NavLastChildNavWindow = nav_window;
7056}
7057
7058// Call when we are expected to land on Layer 0 after FocusWindow()
7059static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7060{
7061 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7062}
7063
7064static void NavRestoreLayer(int layer)
7065{
7066 ImGuiContext& g = *GImGui;
7067 g.NavLayer = layer;
7068 if (layer == 0)
7069 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(window: g.NavWindow);
7070 if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7071 ImGui::SetNavIDWithRectRel(id: g.NavWindow->NavLastIds[0], nav_layer: layer, rect_rel: g.NavWindow->NavRectRel[0]);
7072 else
7073 ImGui::NavInitWindow(window: g.NavWindow, force_reinit: true);
7074}
7075
7076static inline void ImGui::NavUpdateAnyRequestFlag()
7077{
7078 ImGuiContext& g = *GImGui;
7079 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7080 if (g.NavAnyRequest)
7081 IM_ASSERT(g.NavWindow != NULL);
7082}
7083
7084// This needs to be called before we submit any widget (aka in or before Begin)
7085void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7086{
7087 ImGuiContext& g = *GImGui;
7088 IM_ASSERT(window == g.NavWindow);
7089 bool init_for_nav = false;
7090 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7091 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7092 init_for_nav = true;
7093 if (init_for_nav)
7094 {
7095 SetNavID(id: 0, nav_layer: g.NavLayer);
7096 g.NavInitRequest = true;
7097 g.NavInitRequestFromMove = false;
7098 g.NavInitResultId = 0;
7099 g.NavInitResultRectRel = ImRect();
7100 NavUpdateAnyRequestFlag();
7101 }
7102 else
7103 {
7104 g.NavId = window->NavLastIds[0];
7105 }
7106}
7107
7108static ImVec2 ImGui::NavCalcPreferredRefPos()
7109{
7110 ImGuiContext& g = *GImGui;
7111 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7112 {
7113 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
7114 if (IsMousePosValid(mouse_pos: &g.IO.MousePos))
7115 return g.IO.MousePos;
7116 return g.LastValidMousePos;
7117 }
7118 else
7119 {
7120 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
7121 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7122 ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(lhs: g.Style.FramePadding.x * 4, rhs: rect_rel.GetWidth()), rect_rel.Max.y - ImMin(lhs: g.Style.FramePadding.y, rhs: rect_rel.GetHeight()));
7123 ImRect visible_rect = GetViewportRect();
7124 return ImFloor(v: ImClamp(v: pos, mn: visible_rect.Min, mx: visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
7125 }
7126}
7127
7128float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7129{
7130 ImGuiContext& g = *GImGui;
7131 if (mode == ImGuiInputReadMode_Down)
7132 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
7133
7134 const float t = g.IO.NavInputsDownDuration[n];
7135 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
7136 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7137 if (t < 0.0f)
7138 return 0.0f;
7139 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
7140 return (t == 0.0f) ? 1.0f : 0.0f;
7141 if (mode == ImGuiInputReadMode_Repeat)
7142 return (float)CalcTypematicPressedRepeatAmount(t, t_prev: t - g.IO.DeltaTime, repeat_delay: g.IO.KeyRepeatDelay * 0.80f, repeat_rate: g.IO.KeyRepeatRate * 0.80f);
7143 if (mode == ImGuiInputReadMode_RepeatSlow)
7144 return (float)CalcTypematicPressedRepeatAmount(t, t_prev: t - g.IO.DeltaTime, repeat_delay: g.IO.KeyRepeatDelay * 1.00f, repeat_rate: g.IO.KeyRepeatRate * 2.00f);
7145 if (mode == ImGuiInputReadMode_RepeatFast)
7146 return (float)CalcTypematicPressedRepeatAmount(t, t_prev: t - g.IO.DeltaTime, repeat_delay: g.IO.KeyRepeatDelay * 0.80f, repeat_rate: g.IO.KeyRepeatRate * 0.30f);
7147 return 0.0f;
7148}
7149
7150ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7151{
7152 ImVec2 delta(0.0f, 0.0f);
7153 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7154 delta += ImVec2(GetNavInputAmount(n: ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(n: ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(n: ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(n: ImGuiNavInput_KeyUp_, mode));
7155 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7156 delta += ImVec2(GetNavInputAmount(n: ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(n: ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(n: ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(n: ImGuiNavInput_DpadUp, mode));
7157 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7158 delta += ImVec2(GetNavInputAmount(n: ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(n: ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(n: ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(n: ImGuiNavInput_LStickUp, mode));
7159 if (slow_factor != 0.0f && IsNavInputDown(n: ImGuiNavInput_TweakSlow))
7160 delta *= slow_factor;
7161 if (fast_factor != 0.0f && IsNavInputDown(n: ImGuiNavInput_TweakFast))
7162 delta *= fast_factor;
7163 return delta;
7164}
7165
7166// Scroll to keep newly navigated item fully into view
7167// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
7168static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7169{
7170 ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7171 //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7172 if (window_rect.Contains(r: item_rect))
7173 return;
7174
7175 ImGuiContext& g = *GImGui;
7176 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7177 {
7178 window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7179 window->ScrollTargetCenterRatio.x = 0.0f;
7180 }
7181 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7182 {
7183 window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7184 window->ScrollTargetCenterRatio.x = 1.0f;
7185 }
7186 if (item_rect.Min.y < window_rect.Min.y)
7187 {
7188 window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7189 window->ScrollTargetCenterRatio.y = 0.0f;
7190 }
7191 else if (item_rect.Max.y >= window_rect.Max.y)
7192 {
7193 window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7194 window->ScrollTargetCenterRatio.y = 1.0f;
7195 }
7196}
7197
7198static void ImGui::NavUpdate()
7199{
7200 ImGuiContext& g = *GImGui;
7201 g.IO.WantSetMousePos = false;
7202#if 0
7203 if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
7204#endif
7205
7206 // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7207 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7208 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7209 if (nav_gamepad_active)
7210 if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
7211 g.NavInputSource = ImGuiInputSource_NavGamepad;
7212
7213 // Update Keyboard->Nav inputs mapping
7214 if (nav_keyboard_active)
7215 {
7216 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7217 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
7218 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
7219 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
7220 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7221 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7222 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
7223 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7224 if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7225 if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7226 if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
7227 #undef NAV_MAP_KEY
7228 }
7229 memcpy(dest: g.IO.NavInputsDownDurationPrev, src: g.IO.NavInputsDownDuration, n: sizeof(g.IO.NavInputsDownDuration));
7230 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7231 g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
7232
7233 // Process navigation init request (select first/default focus)
7234 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7235 {
7236 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7237 IM_ASSERT(g.NavWindow);
7238 if (g.NavInitRequestFromMove)
7239 SetNavIDWithRectRel(id: g.NavInitResultId, nav_layer: g.NavLayer, rect_rel: g.NavInitResultRectRel);
7240 else
7241 SetNavID(id: g.NavInitResultId, nav_layer: g.NavLayer);
7242 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7243 }
7244 g.NavInitRequest = false;
7245 g.NavInitRequestFromMove = false;
7246 g.NavInitResultId = 0;
7247 g.NavJustMovedToId = 0;
7248
7249 // Process navigation move request
7250 if (g.NavMoveRequest)
7251 NavUpdateMoveResult();
7252
7253 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7254 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7255 {
7256 IM_ASSERT(g.NavMoveRequest);
7257 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7258 g.NavDisableHighlight = false;
7259 g.NavMoveRequestForward = ImGuiNavForward_None;
7260 }
7261
7262 // Apply application mouse position movement, after we had a chance to process move request result.
7263 if (g.NavMousePosDirty && g.NavIdIsAlive)
7264 {
7265 // Set mouse position given our knowledge of the navigated item position from last frame
7266 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7267 {
7268 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7269 {
7270 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7271 g.IO.WantSetMousePos = true;
7272 }
7273 }
7274 g.NavMousePosDirty = false;
7275 }
7276 g.NavIdIsAlive = false;
7277 g.NavJustTabbedId = 0;
7278 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7279
7280 // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
7281 if (g.NavWindow)
7282 NavSaveLastChildNavWindow(nav_window: g.NavWindow);
7283 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7284 g.NavWindow->NavLastChildNavWindow = NULL;
7285
7286 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7287 NavUpdateWindowing();
7288
7289 // Set output flags for user application
7290 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7291 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
7292
7293 // Process NavCancel input (to close a popup, get back to parent, clear focus)
7294 if (IsNavInputPressed(n: ImGuiNavInput_Cancel, mode: ImGuiInputReadMode_Pressed))
7295 {
7296 if (g.ActiveId != 0)
7297 {
7298 ClearActiveID();
7299 }
7300 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7301 {
7302 // Exit child window
7303 ImGuiWindow* child_window = g.NavWindow;
7304 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7305 IM_ASSERT(child_window->ChildId != 0);
7306 FocusWindow(window: parent_window);
7307 SetNavID(id: child_window->ChildId, nav_layer: 0);
7308 g.NavIdIsAlive = false;
7309 if (g.NavDisableMouseHover)
7310 g.NavMousePosDirty = true;
7311 }
7312 else if (g.OpenPopupStack.Size > 0)
7313 {
7314 // Close open popup/menu
7315 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7316 ClosePopupToLevel(remaining: g.OpenPopupStack.Size - 1);
7317 }
7318 else if (g.NavLayer != 0)
7319 {
7320 // Leave the "menu" layer
7321 NavRestoreLayer(layer: 0);
7322 }
7323 else
7324 {
7325 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7326 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7327 g.NavWindow->NavLastIds[0] = 0;
7328 g.NavId = 0;
7329 }
7330 }
7331
7332 // Process manual activation request
7333 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7334 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7335 {
7336 bool activate_down = IsNavInputDown(n: ImGuiNavInput_Activate);
7337 bool activate_pressed = activate_down && IsNavInputPressed(n: ImGuiNavInput_Activate, mode: ImGuiInputReadMode_Pressed);
7338 if (g.ActiveId == 0 && activate_pressed)
7339 g.NavActivateId = g.NavId;
7340 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7341 g.NavActivateDownId = g.NavId;
7342 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7343 g.NavActivatePressedId = g.NavId;
7344 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(n: ImGuiNavInput_Input, mode: ImGuiInputReadMode_Pressed))
7345 g.NavInputId = g.NavId;
7346 }
7347 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7348 g.NavDisableHighlight = true;
7349 if (g.NavActivateId != 0)
7350 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7351 g.NavMoveRequest = false;
7352
7353 // Process programmatic activation request
7354 if (g.NavNextActivateId != 0)
7355 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7356 g.NavNextActivateId = 0;
7357
7358 // Initiate directional inputs request
7359 const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7360 if (g.NavMoveRequestForward == ImGuiNavForward_None)
7361 {
7362 g.NavMoveDir = ImGuiDir_None;
7363 g.NavMoveRequestFlags = 0;
7364 if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7365 {
7366 if ((allowed_dir_flags & (1<<ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(n1: ImGuiNavInput_DpadLeft, n2: ImGuiNavInput_KeyLeft_, mode: ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7367 if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(n1: ImGuiNavInput_DpadRight,n2: ImGuiNavInput_KeyRight_,mode: ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7368 if ((allowed_dir_flags & (1<<ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(n1: ImGuiNavInput_DpadUp, n2: ImGuiNavInput_KeyUp_, mode: ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7369 if ((allowed_dir_flags & (1<<ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(n1: ImGuiNavInput_DpadDown, n2: ImGuiNavInput_KeyDown_, mode: ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7370 }
7371 g.NavMoveClipDir = g.NavMoveDir;
7372 }
7373 else
7374 {
7375 // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
7376 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7377 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7378 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7379 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7380 }
7381
7382 // Update PageUp/PageDown scroll
7383 float nav_scoring_rect_offset_y = 0.0f;
7384 if (nav_keyboard_active)
7385 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7386
7387 // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
7388 if (g.NavMoveDir != ImGuiDir_None)
7389 {
7390 g.NavMoveRequest = true;
7391 g.NavMoveDirLast = g.NavMoveDir;
7392 }
7393 if (g.NavMoveRequest && g.NavId == 0)
7394 {
7395 g.NavInitRequest = g.NavInitRequestFromMove = true;
7396 g.NavInitResultId = 0;
7397 g.NavDisableHighlight = false;
7398 }
7399 NavUpdateAnyRequestFlag();
7400
7401 // Scrolling
7402 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7403 {
7404 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7405 ImGuiWindow* window = g.NavWindow;
7406 const float scroll_speed = ImFloor(f: window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
7407 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7408 {
7409 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7410 SetWindowScrollX(window, new_scroll_x: ImFloor(f: window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7411 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7412 SetWindowScrollY(window, new_scroll_y: ImFloor(f: window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7413 }
7414
7415 // *Normal* Manual scroll with NavScrollXXX keys
7416 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7417 ImVec2 scroll_dir = GetNavInputAmount2d(dir_sources: ImGuiNavDirSourceFlags_PadLStick, mode: ImGuiInputReadMode_Down, slow_factor: 1.0f/10.0f, fast_factor: 10.0f);
7418 if (scroll_dir.x != 0.0f && window->ScrollbarX)
7419 {
7420 SetWindowScrollX(window, new_scroll_x: ImFloor(f: window->Scroll.x + scroll_dir.x * scroll_speed));
7421 g.NavMoveFromClampedRefRect = true;
7422 }
7423 if (scroll_dir.y != 0.0f)
7424 {
7425 SetWindowScrollY(window, new_scroll_y: ImFloor(f: window->Scroll.y + scroll_dir.y * scroll_speed));
7426 g.NavMoveFromClampedRefRect = true;
7427 }
7428 }
7429
7430 // Reset search results
7431 g.NavMoveResultLocal.Clear();
7432 g.NavMoveResultLocalVisibleSet.Clear();
7433 g.NavMoveResultOther.Clear();
7434
7435 // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
7436 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7437 {
7438 ImGuiWindow* window = g.NavWindow;
7439 ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7440 if (!window_rect_rel.Contains(r: window->NavRectRel[g.NavLayer]))
7441 {
7442 float pad = window->CalcFontSize() * 0.5f;
7443 window_rect_rel.Expand(amount: ImVec2(-ImMin(lhs: window_rect_rel.GetWidth(), rhs: pad), -ImMin(lhs: window_rect_rel.GetHeight(), rhs: pad))); // Terrible approximation for the intent of starting navigation from first fully visible item
7444 window->NavRectRel[g.NavLayer].ClipWith(r: window_rect_rel);
7445 g.NavId = 0;
7446 }
7447 g.NavMoveFromClampedRefRect = false;
7448 }
7449
7450 // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
7451 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7452 g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7453 g.NavScoringRectScreen.TranslateY(dy: nav_scoring_rect_offset_y);
7454 g.NavScoringRectScreen.Min.x = ImMin(lhs: g.NavScoringRectScreen.Min.x + 1.0f, rhs: g.NavScoringRectScreen.Max.x);
7455 g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7456 IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
7457 //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7458 g.NavScoringCount = 0;
7459#if IMGUI_DEBUG_NAV_RECTS
7460 if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
7461 if (g.NavWindow) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
7462#endif
7463}
7464
7465// Apply result from previous frame navigation directional move request
7466static void ImGui::NavUpdateMoveResult()
7467{
7468 ImGuiContext& g = *GImGui;
7469 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7470 {
7471 // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
7472 if (g.NavId != 0)
7473 {
7474 g.NavDisableHighlight = false;
7475 g.NavDisableMouseHover = true;
7476 }
7477 return;
7478 }
7479
7480 // Select which result to use
7481 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7482
7483 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7484 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7485 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7486 result = &g.NavMoveResultLocalVisibleSet;
7487
7488 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7489 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7490 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7491 result = &g.NavMoveResultOther;
7492 IM_ASSERT(g.NavWindow && result->Window);
7493
7494 // Scroll to keep newly navigated item fully into view.
7495 if (g.NavLayer == 0)
7496 {
7497 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7498 NavScrollToBringItemIntoView(window: result->Window, item_rect: rect_abs);
7499
7500 // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7501 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window: result->Window, snap_on_edges: false);
7502 ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7503 result->RectRel.Translate(d: delta_scroll);
7504
7505 // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7506 if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7507 NavScrollToBringItemIntoView(window: result->Window->ParentWindow, item_rect: ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7508 }
7509
7510 ClearActiveID();
7511 g.NavWindow = result->Window;
7512 SetNavIDWithRectRel(id: result->ID, nav_layer: g.NavLayer, rect_rel: result->RectRel);
7513 g.NavJustMovedToId = result->ID;
7514 g.NavMoveFromClampedRefRect = false;
7515}
7516
7517static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7518{
7519 ImGuiContext& g = *GImGui;
7520 if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7521 {
7522 ImGuiWindow* window = g.NavWindow;
7523 bool page_up_held = IsKeyDown(user_key_index: g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7524 bool page_down_held = IsKeyDown(user_key_index: g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7525 if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held))
7526 {
7527 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7528 {
7529 // Fallback manual-scroll when window has no navigable item
7530 if (IsKeyPressed(user_key_index: g.IO.KeyMap[ImGuiKey_PageUp], repeat: true))
7531 SetWindowScrollY(window, new_scroll_y: window->Scroll.y - window->InnerClipRect.GetHeight());
7532 else if (IsKeyPressed(user_key_index: g.IO.KeyMap[ImGuiKey_PageDown], repeat: true))
7533 SetWindowScrollY(window, new_scroll_y: window->Scroll.y + window->InnerClipRect.GetHeight());
7534 }
7535 else
7536 {
7537 const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7538 const float page_offset_y = ImMax(lhs: 0.0f, rhs: window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7539 float nav_scoring_rect_offset_y = 0.0f;
7540 if (IsKeyPressed(user_key_index: g.IO.KeyMap[ImGuiKey_PageUp], repeat: true))
7541 {
7542 nav_scoring_rect_offset_y = -page_offset_y;
7543 g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7544 g.NavMoveClipDir = ImGuiDir_Up;
7545 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7546 }
7547 else if (IsKeyPressed(user_key_index: g.IO.KeyMap[ImGuiKey_PageDown], repeat: true))
7548 {
7549 nav_scoring_rect_offset_y = +page_offset_y;
7550 g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7551 g.NavMoveClipDir = ImGuiDir_Down;
7552 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7553 }
7554 return nav_scoring_rect_offset_y;
7555 }
7556 }
7557 }
7558 return 0.0f;
7559}
7560
7561static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7562{
7563 ImGuiContext& g = *GImGui;
7564 for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
7565 if (g.WindowsFocusOrder[i] == window)
7566 return i;
7567 return -1;
7568}
7569
7570static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7571{
7572 ImGuiContext& g = *GImGui;
7573 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
7574 if (ImGui::IsWindowNavFocusable(window: g.WindowsFocusOrder[i]))
7575 return g.WindowsFocusOrder[i];
7576 return NULL;
7577}
7578
7579static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7580{
7581 ImGuiContext& g = *GImGui;
7582 IM_ASSERT(g.NavWindowingTarget);
7583 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7584 return;
7585
7586 const int i_current = FindWindowFocusIndex(window: g.NavWindowingTarget);
7587 ImGuiWindow* window_target = FindWindowNavFocusable(i_start: i_current + focus_change_dir, i_stop: -INT_MAX, dir: focus_change_dir);
7588 if (!window_target)
7589 window_target = FindWindowNavFocusable(i_start: (focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_stop: i_current, dir: focus_change_dir);
7590 if (window_target) // Don't reset windowing target if there's a single window in the list
7591 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7592 g.NavWindowingToggleLayer = false;
7593}
7594
7595// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
7596static void ImGui::NavUpdateWindowing()
7597{
7598 ImGuiContext& g = *GImGui;
7599 ImGuiWindow* apply_focus_window = NULL;
7600 bool apply_toggle_layer = false;
7601
7602 ImGuiWindow* modal_window = GetFrontMostPopupModal();
7603 if (modal_window != NULL)
7604 {
7605 g.NavWindowingTarget = NULL;
7606 return;
7607 }
7608
7609 // Fade out
7610 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7611 {
7612 g.NavWindowingHighlightAlpha = ImMax(lhs: g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, rhs: 0.0f);
7613 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7614 g.NavWindowingTargetAnim = NULL;
7615 }
7616
7617 // Start CTRL-TAB or Square+L/R window selection
7618 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(n: ImGuiNavInput_Menu, mode: ImGuiInputReadMode_Pressed);
7619 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(key: ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7620 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7621 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(i_start: g.WindowsFocusOrder.Size - 1, i_stop: -INT_MAX, dir: -1))
7622 {
7623 g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
7624 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
7625 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
7626 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
7627 }
7628
7629 // Gamepad update
7630 g.NavWindowingTimer += g.IO.DeltaTime;
7631 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
7632 {
7633 // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
7634 g.NavWindowingHighlightAlpha = ImMax(lhs: g.NavWindowingHighlightAlpha, rhs: ImSaturate(f: (g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
7635
7636 // Select window to focus
7637 const int focus_change_dir = (int)IsNavInputPressed(n: ImGuiNavInput_FocusPrev, mode: ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(n: ImGuiNavInput_FocusNext, mode: ImGuiInputReadMode_RepeatSlow);
7638 if (focus_change_dir != 0)
7639 {
7640 NavUpdateWindowingHighlightWindow(focus_change_dir);
7641 g.NavWindowingHighlightAlpha = 1.0f;
7642 }
7643
7644 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
7645 if (!IsNavInputDown(n: ImGuiNavInput_Menu))
7646 {
7647 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
7648 if (g.NavWindowingToggleLayer && g.NavWindow)
7649 apply_toggle_layer = true;
7650 else if (!g.NavWindowingToggleLayer)
7651 apply_focus_window = g.NavWindowingTarget;
7652 g.NavWindowingTarget = NULL;
7653 }
7654 }
7655
7656 // Keyboard: Focus
7657 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
7658 {
7659 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
7660 g.NavWindowingHighlightAlpha = ImMax(lhs: g.NavWindowingHighlightAlpha, rhs: ImSaturate(f: (g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
7661 if (IsKeyPressedMap(key: ImGuiKey_Tab, repeat: true))
7662 NavUpdateWindowingHighlightWindow(focus_change_dir: g.IO.KeyShift ? +1 : -1);
7663 if (!g.IO.KeyCtrl)
7664 apply_focus_window = g.NavWindowingTarget;
7665 }
7666
7667 // Keyboard: Press and Release ALT to toggle menu layer
7668 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
7669 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(n: ImGuiNavInput_KeyMenu_, mode: ImGuiInputReadMode_Released))
7670 if (IsMousePosValid(mouse_pos: &g.IO.MousePos) == IsMousePosValid(mouse_pos: &g.IO.MousePosPrev))
7671 apply_toggle_layer = true;
7672
7673 // Move window
7674 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
7675 {
7676 ImVec2 move_delta;
7677 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
7678 move_delta = GetNavInputAmount2d(dir_sources: ImGuiNavDirSourceFlags_Keyboard, mode: ImGuiInputReadMode_Down);
7679 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
7680 move_delta = GetNavInputAmount2d(dir_sources: ImGuiNavDirSourceFlags_PadLStick, mode: ImGuiInputReadMode_Down);
7681 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
7682 {
7683 const float NAV_MOVE_SPEED = 800.0f;
7684 const float move_speed = ImFloor(f: NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(lhs: g.IO.DisplayFramebufferScale.x, rhs: g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
7685 g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
7686 g.NavDisableMouseHover = true;
7687 MarkIniSettingsDirty(window: g.NavWindowingTarget);
7688 }
7689 }
7690
7691 // Apply final focus
7692 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
7693 {
7694 g.NavDisableHighlight = false;
7695 g.NavDisableMouseHover = true;
7696 apply_focus_window = NavRestoreLastChildNavWindow(window: apply_focus_window);
7697 ClosePopupsOverWindow(ref_window: apply_focus_window);
7698 FocusWindow(window: apply_focus_window);
7699 if (apply_focus_window->NavLastIds[0] == 0)
7700 NavInitWindow(window: apply_focus_window, force_reinit: false);
7701
7702 // If the window only has a menu layer, select it directly
7703 if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1))
7704 g.NavLayer = 1;
7705 }
7706 if (apply_focus_window)
7707 g.NavWindowingTarget = NULL;
7708
7709 // Apply menu/layer toggle
7710 if (apply_toggle_layer && g.NavWindow)
7711 {
7712 // Move to parent menu if necessary
7713 ImGuiWindow* new_nav_window = g.NavWindow;
7714 while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7715 new_nav_window = new_nav_window->ParentWindow;
7716 if (new_nav_window != g.NavWindow)
7717 {
7718 ImGuiWindow* old_nav_window = g.NavWindow;
7719 FocusWindow(window: new_nav_window);
7720 new_nav_window->NavLastChildNavWindow = old_nav_window;
7721 }
7722 g.NavDisableHighlight = false;
7723 g.NavDisableMouseHover = true;
7724 NavRestoreLayer(layer: (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0);
7725 }
7726}
7727
7728// Window has already passed the IsWindowNavFocusable()
7729static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
7730{
7731 if (window->Flags & ImGuiWindowFlags_Popup)
7732 return "(Popup)";
7733 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(s1: window->Name, s2: "##MainMenuBar") == 0)
7734 return "(Main menu bar)";
7735 return "(Untitled)";
7736}
7737
7738// Overlay displayed when using CTRL+TAB. Called by EndFrame().
7739void ImGui::NavUpdateWindowingList()
7740{
7741 ImGuiContext& g = *GImGui;
7742 IM_ASSERT(g.NavWindowingTarget != NULL);
7743
7744 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
7745 return;
7746
7747 if (g.NavWindowingList == NULL)
7748 g.NavWindowingList = FindWindowByName(name: "###NavWindowingList");
7749 SetNextWindowSizeConstraints(size_min: ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), size_max: ImVec2(FLT_MAX, FLT_MAX));
7750 SetNextWindowPos(pos: g.IO.DisplaySize * 0.5f, cond: ImGuiCond_Always, pivot: ImVec2(0.5f, 0.5f));
7751 PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: g.Style.WindowPadding * 2.0f);
7752 Begin(name: "###NavWindowingList", NULL, flags: ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
7753 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
7754 {
7755 ImGuiWindow* window = g.WindowsFocusOrder[n];
7756 if (!IsWindowNavFocusable(window))
7757 continue;
7758 const char* label = window->Name;
7759 if (label == FindRenderedTextEnd(text: label))
7760 label = GetFallbackWindowNameForWindowingList(window);
7761 Selectable(label, selected: g.NavWindowingTarget == window);
7762 }
7763 End();
7764 PopStyleVar();
7765}
7766
7767//-----------------------------------------------------------------------------
7768// [SECTION] COLUMNS
7769// In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
7770//-----------------------------------------------------------------------------
7771
7772void ImGui::NextColumn()
7773{
7774 ImGuiWindow* window = GetCurrentWindow();
7775 if (window->SkipItems || window->DC.ColumnsSet == NULL)
7776 return;
7777
7778 ImGuiContext& g = *GImGui;
7779 PopItemWidth();
7780 PopClipRect();
7781
7782 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7783 columns->LineMaxY = ImMax(lhs: columns->LineMaxY, rhs: window->DC.CursorPos.y);
7784 if (++columns->Current < columns->Count)
7785 {
7786 // Columns 1+ cancel out IndentX
7787 window->DC.ColumnsOffset.x = GetColumnOffset(column_index: columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
7788 window->DrawList->ChannelsSetCurrent(channel_index: columns->Current);
7789 }
7790 else
7791 {
7792 window->DC.ColumnsOffset.x = 0.0f;
7793 window->DrawList->ChannelsSetCurrent(channel_index: 0);
7794 columns->Current = 0;
7795 columns->LineMinY = columns->LineMaxY;
7796 }
7797 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7798 window->DC.CursorPos.y = columns->LineMinY;
7799 window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
7800 window->DC.CurrentLineTextBaseOffset = 0.0f;
7801
7802 PushColumnClipRect();
7803 PushItemWidth(item_width: GetColumnWidth() * 0.65f); // FIXME: Move on columns setup
7804}
7805
7806int ImGui::GetColumnIndex()
7807{
7808 ImGuiWindow* window = GetCurrentWindowRead();
7809 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
7810}
7811
7812int ImGui::GetColumnsCount()
7813{
7814 ImGuiWindow* window = GetCurrentWindowRead();
7815 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
7816}
7817
7818static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
7819{
7820 return offset_norm * (columns->MaxX - columns->MinX);
7821}
7822
7823static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
7824{
7825 return offset / (columns->MaxX - columns->MinX);
7826}
7827
7828static inline float GetColumnsRectHalfWidth() { return 4.0f; }
7829
7830static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
7831{
7832 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
7833 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
7834 ImGuiContext& g = *GImGui;
7835 ImGuiWindow* window = g.CurrentWindow;
7836 IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
7837 IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
7838
7839 float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
7840 x = ImMax(lhs: x, rhs: ImGui::GetColumnOffset(column_index: column_index - 1) + g.Style.ColumnsMinSpacing);
7841 if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
7842 x = ImMin(lhs: x, rhs: ImGui::GetColumnOffset(column_index: column_index + 1) - g.Style.ColumnsMinSpacing);
7843
7844 return x;
7845}
7846
7847float ImGui::GetColumnOffset(int column_index)
7848{
7849 ImGuiWindow* window = GetCurrentWindowRead();
7850 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7851 IM_ASSERT(columns != NULL);
7852
7853 if (column_index < 0)
7854 column_index = columns->Current;
7855 IM_ASSERT(column_index < columns->Columns.Size);
7856
7857 const float t = columns->Columns[column_index].OffsetNorm;
7858 const float x_offset = ImLerp(a: columns->MinX, b: columns->MaxX, t);
7859 return x_offset;
7860}
7861
7862static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
7863{
7864 if (column_index < 0)
7865 column_index = columns->Current;
7866
7867 float offset_norm;
7868 if (before_resize)
7869 offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
7870 else
7871 offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
7872 return OffsetNormToPixels(columns, offset_norm);
7873}
7874
7875float ImGui::GetColumnWidth(int column_index)
7876{
7877 ImGuiWindow* window = GetCurrentWindowRead();
7878 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7879 IM_ASSERT(columns != NULL);
7880
7881 if (column_index < 0)
7882 column_index = columns->Current;
7883 return OffsetNormToPixels(columns, offset_norm: columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
7884}
7885
7886void ImGui::SetColumnOffset(int column_index, float offset)
7887{
7888 ImGuiContext& g = *GImGui;
7889 ImGuiWindow* window = g.CurrentWindow;
7890 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7891 IM_ASSERT(columns != NULL);
7892
7893 if (column_index < 0)
7894 column_index = columns->Current;
7895 IM_ASSERT(column_index < columns->Columns.Size);
7896
7897 const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
7898 const float width = preserve_width ? GetColumnWidthEx(columns, column_index, before_resize: columns->IsBeingResized) : 0.0f;
7899
7900 if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
7901 offset = ImMin(lhs: offset, rhs: columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
7902 columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset: offset - columns->MinX);
7903
7904 if (preserve_width)
7905 SetColumnOffset(column_index: column_index + 1, offset: offset + ImMax(lhs: g.Style.ColumnsMinSpacing, rhs: width));
7906}
7907
7908void ImGui::SetColumnWidth(int column_index, float width)
7909{
7910 ImGuiWindow* window = GetCurrentWindowRead();
7911 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7912 IM_ASSERT(columns != NULL);
7913
7914 if (column_index < 0)
7915 column_index = columns->Current;
7916 SetColumnOffset(column_index: column_index + 1, offset: GetColumnOffset(column_index) + width);
7917}
7918
7919void ImGui::PushColumnClipRect(int column_index)
7920{
7921 ImGuiWindow* window = GetCurrentWindowRead();
7922 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7923 if (column_index < 0)
7924 column_index = columns->Current;
7925
7926 PushClipRect(clip_rect_min: columns->Columns[column_index].ClipRect.Min, clip_rect_max: columns->Columns[column_index].ClipRect.Max, intersect_with_current_clip_rect: false);
7927}
7928
7929static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
7930{
7931 for (int n = 0; n < window->ColumnsStorage.Size; n++)
7932 if (window->ColumnsStorage[n].ID == id)
7933 return &window->ColumnsStorage[n];
7934
7935 window->ColumnsStorage.push_back(v: ImGuiColumnsSet());
7936 ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
7937 columns->ID = id;
7938 return columns;
7939}
7940
7941void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
7942{
7943 ImGuiContext& g = *GImGui;
7944 ImGuiWindow* window = GetCurrentWindow();
7945
7946 IM_ASSERT(columns_count > 1);
7947 IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
7948
7949 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
7950 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
7951 PushID(int_id: 0x11223347 + (str_id ? 0 : columns_count));
7952 ImGuiID id = window->GetID(str: str_id ? str_id : "columns");
7953 PopID();
7954
7955 // Acquire storage for the columns set
7956 ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
7957 IM_ASSERT(columns->ID == id);
7958 columns->Current = 0;
7959 columns->Count = columns_count;
7960 columns->Flags = flags;
7961 window->DC.ColumnsSet = columns;
7962
7963 // Set state for first column
7964 const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
7965 columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
7966 columns->MaxX = ImMax(lhs: content_region_width - window->Scroll.x, rhs: columns->MinX + 1.0f);
7967 columns->StartPosY = window->DC.CursorPos.y;
7968 columns->StartMaxPosX = window->DC.CursorMaxPos.x;
7969 columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
7970 window->DC.ColumnsOffset.x = 0.0f;
7971 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7972
7973 // Clear data if columns count changed
7974 if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
7975 columns->Columns.resize(new_size: 0);
7976
7977 // Initialize defaults
7978 columns->IsFirstFrame = (columns->Columns.Size == 0);
7979 if (columns->Columns.Size == 0)
7980 {
7981 columns->Columns.reserve(new_capacity: columns_count + 1);
7982 for (int n = 0; n < columns_count + 1; n++)
7983 {
7984 ImGuiColumnData column;
7985 column.OffsetNorm = n / (float)columns_count;
7986 columns->Columns.push_back(v: column);
7987 }
7988 }
7989
7990 for (int n = 0; n < columns_count; n++)
7991 {
7992 // Compute clipping rectangle
7993 ImGuiColumnData* column = &columns->Columns[n];
7994 float clip_x1 = ImFloor(f: 0.5f + window->Pos.x + GetColumnOffset(column_index: n) - 1.0f);
7995 float clip_x2 = ImFloor(f: 0.5f + window->Pos.x + GetColumnOffset(column_index: n + 1) - 1.0f);
7996 column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
7997 column->ClipRect.ClipWith(r: window->ClipRect);
7998 }
7999
8000 window->DrawList->ChannelsSplit(channels_count: columns->Count);
8001 PushColumnClipRect();
8002 PushItemWidth(item_width: GetColumnWidth() * 0.65f);
8003}
8004
8005void ImGui::EndColumns()
8006{
8007 ImGuiContext& g = *GImGui;
8008 ImGuiWindow* window = GetCurrentWindow();
8009 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8010 IM_ASSERT(columns != NULL);
8011
8012 PopItemWidth();
8013 PopClipRect();
8014 window->DrawList->ChannelsMerge();
8015
8016 columns->LineMaxY = ImMax(lhs: columns->LineMaxY, rhs: window->DC.CursorPos.y);
8017 window->DC.CursorPos.y = columns->LineMaxY;
8018 if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
8019 window->DC.CursorMaxPos.x = columns->StartMaxPosX; // Restore cursor max pos, as columns don't grow parent
8020
8021 // Draw columns borders and handle resize
8022 bool is_being_resized = false;
8023 if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
8024 {
8025 const float y1 = columns->StartPosY;
8026 const float y2 = window->DC.CursorPos.y;
8027 int dragging_column = -1;
8028 for (int n = 1; n < columns->Count; n++)
8029 {
8030 float x = window->Pos.x + GetColumnOffset(column_index: n);
8031 const ImGuiID column_id = columns->ID + ImGuiID(n);
8032 const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
8033 const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
8034 KeepAliveID(id: column_id);
8035 if (IsClippedEx(bb: column_rect, id: column_id, clip_even_when_logged: false))
8036 continue;
8037
8038 bool hovered = false, held = false;
8039 if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
8040 {
8041 ButtonBehavior(bb: column_rect, id: column_id, out_hovered: &hovered, out_held: &held);
8042 if (hovered || held)
8043 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
8044 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
8045 dragging_column = n;
8046 }
8047
8048 // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
8049 const ImU32 col = GetColorU32(idx: held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
8050 const float xi = (float)(int)x;
8051 window->DrawList->AddLine(a: ImVec2(xi, ImMax(lhs: y1 + 1.0f, rhs: window->ClipRect.Min.y)), b: ImVec2(xi, ImMin(lhs: y2, rhs: window->ClipRect.Max.y)), col);
8052 }
8053
8054 // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
8055 if (dragging_column != -1)
8056 {
8057 if (!columns->IsBeingResized)
8058 for (int n = 0; n < columns->Count + 1; n++)
8059 columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
8060 columns->IsBeingResized = is_being_resized = true;
8061 float x = GetDraggedColumnOffset(columns, column_index: dragging_column);
8062 SetColumnOffset(column_index: dragging_column, offset: x);
8063 }
8064 }
8065 columns->IsBeingResized = is_being_resized;
8066
8067 window->DC.ColumnsSet = NULL;
8068 window->DC.ColumnsOffset.x = 0.0f;
8069 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8070}
8071
8072// [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
8073void ImGui::Columns(int columns_count, const char* id, bool border)
8074{
8075 ImGuiWindow* window = GetCurrentWindow();
8076 IM_ASSERT(columns_count >= 1);
8077
8078 ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
8079 //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
8080 if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
8081 return;
8082
8083 if (window->DC.ColumnsSet != NULL)
8084 EndColumns();
8085
8086 if (columns_count != 1)
8087 BeginColumns(str_id: id, columns_count, flags);
8088}
8089
8090//-----------------------------------------------------------------------------
8091// [SECTION] DRAG AND DROP
8092//-----------------------------------------------------------------------------
8093
8094void ImGui::ClearDragDrop()
8095{
8096 ImGuiContext& g = *GImGui;
8097 g.DragDropActive = false;
8098 g.DragDropPayload.Clear();
8099 g.DragDropAcceptFlags = 0;
8100 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8101 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8102 g.DragDropAcceptFrameCount = -1;
8103
8104 g.DragDropPayloadBufHeap.clear();
8105 memset(s: &g.DragDropPayloadBufLocal, c: 0, n: sizeof(g.DragDropPayloadBufLocal));
8106}
8107
8108// Call when current ID is active.
8109// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
8110bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8111{
8112 ImGuiContext& g = *GImGui;
8113 ImGuiWindow* window = g.CurrentWindow;
8114
8115 bool source_drag_active = false;
8116 ImGuiID source_id = 0;
8117 ImGuiID source_parent_id = 0;
8118 int mouse_button = 0;
8119 if (!(flags & ImGuiDragDropFlags_SourceExtern))
8120 {
8121 source_id = window->DC.LastItemId;
8122 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8123 return false;
8124 if (g.IO.MouseDown[mouse_button] == false)
8125 return false;
8126
8127 if (source_id == 0)
8128 {
8129 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8130 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8131 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8132 {
8133 IM_ASSERT(0);
8134 return false;
8135 }
8136
8137 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8138 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8139 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8140 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8141 bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8142 if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8143 return false;
8144 source_id = window->DC.LastItemId = window->GetIDFromRectangle(r_abs: window->DC.LastItemRect);
8145 if (is_hovered)
8146 SetHoveredID(source_id);
8147 if (is_hovered && g.IO.MouseClicked[mouse_button])
8148 {
8149 SetActiveID(id: source_id, window);
8150 FocusWindow(window);
8151 }
8152 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8153 g.ActiveIdAllowOverlap = is_hovered;
8154 }
8155 else
8156 {
8157 g.ActiveIdAllowOverlap = false;
8158 }
8159 if (g.ActiveId != source_id)
8160 return false;
8161 source_parent_id = window->IDStack.back();
8162 source_drag_active = IsMouseDragging(button: mouse_button);
8163 }
8164 else
8165 {
8166 window = NULL;
8167 source_id = ImHash(data: "#SourceExtern", data_size: 0);
8168 source_drag_active = true;
8169 }
8170
8171 if (source_drag_active)
8172 {
8173 if (!g.DragDropActive)
8174 {
8175 IM_ASSERT(source_id != 0);
8176 ClearDragDrop();
8177 ImGuiPayload& payload = g.DragDropPayload;
8178 payload.SourceId = source_id;
8179 payload.SourceParentId = source_parent_id;
8180 g.DragDropActive = true;
8181 g.DragDropSourceFlags = flags;
8182 g.DragDropMouseButton = mouse_button;
8183 }
8184 g.DragDropSourceFrameCount = g.FrameCount;
8185 g.DragDropWithinSourceOrTarget = true;
8186
8187 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8188 {
8189 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8190 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8191 BeginTooltip();
8192 if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8193 {
8194 ImGuiWindow* tooltip_window = g.CurrentWindow;
8195 tooltip_window->SkipItems = true;
8196 tooltip_window->HiddenFramesRegular = 1;
8197 }
8198 }
8199
8200 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8201 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8202
8203 return true;
8204 }
8205 return false;
8206}
8207
8208void ImGui::EndDragDropSource()
8209{
8210 ImGuiContext& g = *GImGui;
8211 IM_ASSERT(g.DragDropActive);
8212 IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8213
8214 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8215 EndTooltip();
8216
8217 // Discard the drag if have not called SetDragDropPayload()
8218 if (g.DragDropPayload.DataFrameCount == -1)
8219 ClearDragDrop();
8220 g.DragDropWithinSourceOrTarget = false;
8221}
8222
8223// Use 'cond' to choose to submit payload on drag start or every frame
8224bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8225{
8226 ImGuiContext& g = *GImGui;
8227 ImGuiPayload& payload = g.DragDropPayload;
8228 if (cond == 0)
8229 cond = ImGuiCond_Always;
8230
8231 IM_ASSERT(type != NULL);
8232 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8233 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8234 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8235 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
8236
8237 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8238 {
8239 // Copy payload
8240 ImStrncpy(dst: payload.DataType, src: type, IM_ARRAYSIZE(payload.DataType));
8241 g.DragDropPayloadBufHeap.resize(new_size: 0);
8242 if (data_size > sizeof(g.DragDropPayloadBufLocal))
8243 {
8244 // Store in heap
8245 g.DragDropPayloadBufHeap.resize(new_size: (int)data_size);
8246 payload.Data = g.DragDropPayloadBufHeap.Data;
8247 memcpy(dest: payload.Data, src: data, n: data_size);
8248 }
8249 else if (data_size > 0)
8250 {
8251 // Store locally
8252 memset(s: &g.DragDropPayloadBufLocal, c: 0, n: sizeof(g.DragDropPayloadBufLocal));
8253 payload.Data = g.DragDropPayloadBufLocal;
8254 memcpy(dest: payload.Data, src: data, n: data_size);
8255 }
8256 else
8257 {
8258 payload.Data = NULL;
8259 }
8260 payload.DataSize = (int)data_size;
8261 }
8262 payload.DataFrameCount = g.FrameCount;
8263
8264 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8265}
8266
8267bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8268{
8269 ImGuiContext& g = *GImGui;
8270 if (!g.DragDropActive)
8271 return false;
8272
8273 ImGuiWindow* window = g.CurrentWindow;
8274 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8275 return false;
8276 IM_ASSERT(id != 0);
8277 if (!IsMouseHoveringRect(r_min: bb.Min, r_max: bb.Max) || (id == g.DragDropPayload.SourceId))
8278 return false;
8279 if (window->SkipItems)
8280 return false;
8281
8282 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8283 g.DragDropTargetRect = bb;
8284 g.DragDropTargetId = id;
8285 g.DragDropWithinSourceOrTarget = true;
8286 return true;
8287}
8288
8289// We don't use BeginDragDropTargetCustom() and duplicate its code because:
8290// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8291// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8292// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
8293bool ImGui::BeginDragDropTarget()
8294{
8295 ImGuiContext& g = *GImGui;
8296 if (!g.DragDropActive)
8297 return false;
8298
8299 ImGuiWindow* window = g.CurrentWindow;
8300 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8301 return false;
8302 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8303 return false;
8304
8305 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8306 ImGuiID id = window->DC.LastItemId;
8307 if (id == 0)
8308 id = window->GetIDFromRectangle(r_abs: display_rect);
8309 if (g.DragDropPayload.SourceId == id)
8310 return false;
8311
8312 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8313 g.DragDropTargetRect = display_rect;
8314 g.DragDropTargetId = id;
8315 g.DragDropWithinSourceOrTarget = true;
8316 return true;
8317}
8318
8319bool ImGui::IsDragDropPayloadBeingAccepted()
8320{
8321 ImGuiContext& g = *GImGui;
8322 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8323}
8324
8325const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8326{
8327 ImGuiContext& g = *GImGui;
8328 ImGuiWindow* window = g.CurrentWindow;
8329 ImGuiPayload& payload = g.DragDropPayload;
8330 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8331 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
8332 if (type != NULL && !payload.IsDataType(type))
8333 return NULL;
8334
8335 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8336 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8337 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8338 ImRect r = g.DragDropTargetRect;
8339 float r_surface = r.GetWidth() * r.GetHeight();
8340 if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8341 {
8342 g.DragDropAcceptFlags = flags;
8343 g.DragDropAcceptIdCurr = g.DragDropTargetId;
8344 g.DragDropAcceptIdCurrRectSurface = r_surface;
8345 }
8346
8347 // Render default drop visuals
8348 payload.Preview = was_accepted_previously;
8349 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8350 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8351 {
8352 // FIXME-DRAG: Settle on a proper default visuals for drop target.
8353 r.Expand(amount: 3.5f);
8354 bool push_clip_rect = !window->ClipRect.Contains(r);
8355 if (push_clip_rect) window->DrawList->PushClipRect(clip_rect_min: r.Min-ImVec2(1,1), clip_rect_max: r.Max+ImVec2(1,1));
8356 window->DrawList->AddRect(a: r.Min, b: r.Max, col: GetColorU32(idx: ImGuiCol_DragDropTarget), rounding: 0.0f, rounding_corners_flags: ~0, thickness: 2.0f);
8357 if (push_clip_rect) window->DrawList->PopClipRect();
8358 }
8359
8360 g.DragDropAcceptFrameCount = g.FrameCount;
8361 payload.Delivery = was_accepted_previously && !IsMouseDown(button: g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
8362 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8363 return NULL;
8364
8365 return &payload;
8366}
8367
8368const ImGuiPayload* ImGui::GetDragDropPayload()
8369{
8370 ImGuiContext& g = *GImGui;
8371 return g.DragDropActive ? &g.DragDropPayload : NULL;
8372}
8373
8374// We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
8375void ImGui::EndDragDropTarget()
8376{
8377 ImGuiContext& g = *GImGui;
8378 IM_ASSERT(g.DragDropActive);
8379 IM_ASSERT(g.DragDropWithinSourceOrTarget);
8380 g.DragDropWithinSourceOrTarget = false;
8381}
8382
8383//-----------------------------------------------------------------------------
8384// [SECTION] LOGGING/CAPTURING
8385//-----------------------------------------------------------------------------
8386
8387// Pass text data straight to log (without being displayed)
8388void ImGui::LogText(const char* fmt, ...)
8389{
8390 ImGuiContext& g = *GImGui;
8391 if (!g.LogEnabled)
8392 return;
8393
8394 va_list args;
8395 va_start(args, fmt);
8396 if (g.LogFile)
8397 vfprintf(s: g.LogFile, format: fmt, arg: args);
8398 else
8399 g.LogClipboard.appendfv(fmt, args);
8400 va_end(args);
8401}
8402
8403// Internal version that takes a position to decide on newline placement and pad items according to their depth.
8404// We split text into individual lines to add current tree level padding
8405void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8406{
8407 ImGuiContext& g = *GImGui;
8408 ImGuiWindow* window = g.CurrentWindow;
8409
8410 if (!text_end)
8411 text_end = FindRenderedTextEnd(text, text_end);
8412
8413 const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8414 if (ref_pos)
8415 window->DC.LogLinePosY = ref_pos->y;
8416
8417 const char* text_remaining = text;
8418 if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
8419 g.LogStartDepth = window->DC.TreeDepth;
8420 const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8421 for (;;)
8422 {
8423 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8424 const char* line_start = text_remaining;
8425 const char* line_end = ImStreolRange(str: line_start, str_end: text_end);
8426 const bool is_first_line = (line_start == text);
8427 const bool is_last_line = (line_end == text_end);
8428 if (!is_last_line || (line_start != line_end))
8429 {
8430 const int char_count = (int)(line_end - line_start);
8431 if (log_new_line || !is_first_line)
8432 LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start);
8433 else
8434 LogText(fmt: " %.*s", char_count, line_start);
8435 }
8436
8437 if (is_last_line)
8438 break;
8439 text_remaining = line_end + 1;
8440 }
8441}
8442
8443// Start logging ImGui output to TTY
8444void ImGui::LogToTTY(int max_depth)
8445{
8446 ImGuiContext& g = *GImGui;
8447 if (g.LogEnabled)
8448 return;
8449 ImGuiWindow* window = g.CurrentWindow;
8450
8451 IM_ASSERT(g.LogFile == NULL);
8452 g.LogFile = stdout;
8453 g.LogEnabled = true;
8454 g.LogStartDepth = window->DC.TreeDepth;
8455 if (max_depth >= 0)
8456 g.LogAutoExpandMaxDepth = max_depth;
8457}
8458
8459// Start logging ImGui output to given file
8460void ImGui::LogToFile(int max_depth, const char* filename)
8461{
8462 ImGuiContext& g = *GImGui;
8463 if (g.LogEnabled)
8464 return;
8465 ImGuiWindow* window = g.CurrentWindow;
8466
8467 if (!filename)
8468 {
8469 filename = g.IO.LogFilename;
8470 if (!filename)
8471 return;
8472 }
8473
8474 IM_ASSERT(g.LogFile == NULL);
8475 g.LogFile = ImFileOpen(filename, mode: "ab");
8476 if (!g.LogFile)
8477 {
8478 IM_ASSERT(g.LogFile != NULL); // Consider this an error
8479 return;
8480 }
8481 g.LogEnabled = true;
8482 g.LogStartDepth = window->DC.TreeDepth;
8483 if (max_depth >= 0)
8484 g.LogAutoExpandMaxDepth = max_depth;
8485}
8486
8487// Start logging ImGui output to clipboard
8488void ImGui::LogToClipboard(int max_depth)
8489{
8490 ImGuiContext& g = *GImGui;
8491 if (g.LogEnabled)
8492 return;
8493 ImGuiWindow* window = g.CurrentWindow;
8494
8495 IM_ASSERT(g.LogFile == NULL);
8496 g.LogFile = NULL;
8497 g.LogEnabled = true;
8498 g.LogStartDepth = window->DC.TreeDepth;
8499 if (max_depth >= 0)
8500 g.LogAutoExpandMaxDepth = max_depth;
8501}
8502
8503void ImGui::LogFinish()
8504{
8505 ImGuiContext& g = *GImGui;
8506 if (!g.LogEnabled)
8507 return;
8508
8509 LogText(IM_NEWLINE);
8510 if (g.LogFile != NULL)
8511 {
8512 if (g.LogFile == stdout)
8513 fflush(stream: g.LogFile);
8514 else
8515 fclose(stream: g.LogFile);
8516 g.LogFile = NULL;
8517 }
8518 if (g.LogClipboard.size() > 1)
8519 {
8520 SetClipboardText(g.LogClipboard.begin());
8521 g.LogClipboard.clear();
8522 }
8523 g.LogEnabled = false;
8524}
8525
8526// Helper to display logging buttons
8527void ImGui::LogButtons()
8528{
8529 ImGuiContext& g = *GImGui;
8530
8531 PushID(str_id: "LogButtons");
8532 const bool log_to_tty = Button(label: "Log To TTY"); SameLine();
8533 const bool log_to_file = Button(label: "Log To File"); SameLine();
8534 const bool log_to_clipboard = Button(label: "Log To Clipboard"); SameLine();
8535 PushItemWidth(item_width: 80.0f);
8536 PushAllowKeyboardFocus(allow_keyboard_focus: false);
8537 SliderInt(label: "Depth", v: &g.LogAutoExpandMaxDepth, v_min: 0, v_max: 9, NULL);
8538 PopAllowKeyboardFocus();
8539 PopItemWidth();
8540 PopID();
8541
8542 // Start logging at the end of the function so that the buttons don't appear in the log
8543 if (log_to_tty)
8544 LogToTTY(max_depth: g.LogAutoExpandMaxDepth);
8545 if (log_to_file)
8546 LogToFile(max_depth: g.LogAutoExpandMaxDepth, filename: g.IO.LogFilename);
8547 if (log_to_clipboard)
8548 LogToClipboard(max_depth: g.LogAutoExpandMaxDepth);
8549}
8550
8551//-----------------------------------------------------------------------------
8552// [SECTION] SETTINGS
8553//-----------------------------------------------------------------------------
8554
8555void ImGui::MarkIniSettingsDirty()
8556{
8557 ImGuiContext& g = *GImGui;
8558 if (g.SettingsDirtyTimer <= 0.0f)
8559 g.SettingsDirtyTimer = g.IO.IniSavingRate;
8560}
8561
8562void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8563{
8564 ImGuiContext& g = *GImGui;
8565 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8566 if (g.SettingsDirtyTimer <= 0.0f)
8567 g.SettingsDirtyTimer = g.IO.IniSavingRate;
8568}
8569
8570ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
8571{
8572 ImGuiContext& g = *GImGui;
8573 g.SettingsWindows.push_back(v: ImGuiWindowSettings());
8574 ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8575 settings->Name = ImStrdup(str: name);
8576 settings->ID = ImHash(data: name, data_size: 0);
8577 return settings;
8578}
8579
8580ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8581{
8582 ImGuiContext& g = *GImGui;
8583 for (int i = 0; i != g.SettingsWindows.Size; i++)
8584 if (g.SettingsWindows[i].ID == id)
8585 return &g.SettingsWindows[i];
8586 return NULL;
8587}
8588
8589void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8590{
8591 size_t file_data_size = 0;
8592 char* file_data = (char*)ImFileLoadToMemory(filename: ini_filename, file_open_mode: "rb", out_file_size: &file_data_size);
8593 if (!file_data)
8594 return;
8595 LoadIniSettingsFromMemory(ini_data: file_data, ini_size: (size_t)file_data_size);
8596 ImGui::MemFree(ptr: file_data);
8597}
8598
8599ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8600{
8601 ImGuiContext& g = *GImGui;
8602 const ImGuiID type_hash = ImHash(data: type_name, data_size: 0, seed: 0);
8603 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8604 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
8605 return &g.SettingsHandlers[handler_n];
8606 return NULL;
8607}
8608
8609// Zero-tolerance, no error reporting, cheap .ini parsing
8610void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
8611{
8612 ImGuiContext& g = *GImGui;
8613 IM_ASSERT(g.Initialized);
8614 IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
8615
8616 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
8617 // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
8618 if (ini_size == 0)
8619 ini_size = strlen(s: ini_data);
8620 char* buf = (char*)ImGui::MemAlloc(size: ini_size + 1);
8621 char* buf_end = buf + ini_size;
8622 memcpy(dest: buf, src: ini_data, n: ini_size);
8623 buf[ini_size] = 0;
8624
8625 void* entry_data = NULL;
8626 ImGuiSettingsHandler* entry_handler = NULL;
8627
8628 char* line_end = NULL;
8629 for (char* line = buf; line < buf_end; line = line_end + 1)
8630 {
8631 // Skip new lines markers, then find end of the line
8632 while (*line == '\n' || *line == '\r')
8633 line++;
8634 line_end = line;
8635 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
8636 line_end++;
8637 line_end[0] = 0;
8638 if (line[0] == ';')
8639 continue;
8640 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
8641 {
8642 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
8643 line_end[-1] = 0;
8644 const char* name_end = line_end - 1;
8645 const char* type_start = line + 1;
8646 char* type_end = (char*)(intptr_t)ImStrchrRange(str: type_start, str_end: name_end, c: ']');
8647 const char* name_start = type_end ? ImStrchrRange(str: type_end + 1, str_end: name_end, c: '[') : NULL;
8648 if (!type_end || !name_start)
8649 {
8650 name_start = type_start; // Import legacy entries that have no type
8651 type_start = "Window";
8652 }
8653 else
8654 {
8655 *type_end = 0; // Overwrite first ']'
8656 name_start++; // Skip second '['
8657 }
8658 entry_handler = FindSettingsHandler(type_name: type_start);
8659 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
8660 }
8661 else if (entry_handler != NULL && entry_data != NULL)
8662 {
8663 // Let type handler parse the line
8664 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
8665 }
8666 }
8667 ImGui::MemFree(ptr: buf);
8668 g.SettingsLoaded = true;
8669}
8670
8671void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
8672{
8673 ImGuiContext& g = *GImGui;
8674 g.SettingsDirtyTimer = 0.0f;
8675 if (!ini_filename)
8676 return;
8677
8678 size_t ini_data_size = 0;
8679 const char* ini_data = SaveIniSettingsToMemory(out_ini_size: &ini_data_size);
8680 FILE* f = ImFileOpen(filename: ini_filename, mode: "wt");
8681 if (!f)
8682 return;
8683 fwrite(ptr: ini_data, size: sizeof(char), n: ini_data_size, s: f);
8684 fclose(stream: f);
8685}
8686
8687// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
8688const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
8689{
8690 ImGuiContext& g = *GImGui;
8691 g.SettingsDirtyTimer = 0.0f;
8692 g.SettingsIniData.Buf.resize(new_size: 0);
8693 g.SettingsIniData.Buf.push_back(v: 0);
8694 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8695 {
8696 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
8697 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
8698 }
8699 if (out_size)
8700 *out_size = (size_t)g.SettingsIniData.size();
8701 return g.SettingsIniData.c_str();
8702}
8703
8704static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
8705{
8706 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(id: ImHash(data: name, data_size: 0));
8707 if (!settings)
8708 settings = ImGui::CreateNewWindowSettings(name);
8709 return (void*)settings;
8710}
8711
8712static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
8713{
8714 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
8715 float x, y;
8716 int i;
8717 if (sscanf(s: line, format: "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y);
8718 else if (sscanf(s: line, format: "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(lhs: ImVec2(x, y), rhs: GImGui->Style.WindowMinSize);
8719 else if (sscanf(s: line, format: "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
8720}
8721
8722static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
8723{
8724 // Gather data from windows that were active during this session
8725 // (if a window wasn't opened in this session we preserve its settings)
8726 ImGuiContext& g = *imgui_ctx;
8727 for (int i = 0; i != g.Windows.Size; i++)
8728 {
8729 ImGuiWindow* window = g.Windows[i];
8730 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
8731 continue;
8732
8733 ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(id: window->ID);
8734 if (!settings)
8735 {
8736 settings = ImGui::CreateNewWindowSettings(name: window->Name);
8737 window->SettingsIdx = g.SettingsWindows.index_from_pointer(it: settings);
8738 }
8739 IM_ASSERT(settings->ID == window->ID);
8740 settings->Pos = window->Pos;
8741 settings->Size = window->SizeFull;
8742 settings->Collapsed = window->Collapsed;
8743 }
8744
8745 // Write to text buffer
8746 buf->reserve(capacity: buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
8747 for (int i = 0; i != g.SettingsWindows.Size; i++)
8748 {
8749 const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
8750 if (settings->Pos.x == FLT_MAX)
8751 continue;
8752 const char* name = settings->Name;
8753 if (const char* p = strstr(haystack: name, needle: "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
8754 name = p;
8755 buf->appendf(fmt: "[%s][%s]\n", handler->TypeName, name);
8756 buf->appendf(fmt: "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
8757 buf->appendf(fmt: "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
8758 buf->appendf(fmt: "Collapsed=%d\n", settings->Collapsed);
8759 buf->appendf(fmt: "\n");
8760 }
8761}
8762
8763//-----------------------------------------------------------------------------
8764// [SECTION] PLATFORM DEPENDENT HELPERS
8765//-----------------------------------------------------------------------------
8766
8767#if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
8768#ifndef WIN32_LEAN_AND_MEAN
8769#define WIN32_LEAN_AND_MEAN
8770#endif
8771#ifndef __MINGW32__
8772#include <Windows.h>
8773#else
8774#include <windows.h>
8775#endif
8776#endif
8777
8778// Win32 API clipboard implementation
8779#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
8780
8781#ifdef _MSC_VER
8782#pragma comment(lib, "user32")
8783#endif
8784
8785static const char* GetClipboardTextFn_DefaultImpl(void*)
8786{
8787 static ImVector<char> buf_local;
8788 buf_local.clear();
8789 if (!::OpenClipboard(NULL))
8790 return NULL;
8791 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
8792 if (wbuf_handle == NULL)
8793 {
8794 ::CloseClipboard();
8795 return NULL;
8796 }
8797 if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
8798 {
8799 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
8800 buf_local.resize(buf_len);
8801 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
8802 }
8803 ::GlobalUnlock(wbuf_handle);
8804 ::CloseClipboard();
8805 return buf_local.Data;
8806}
8807
8808static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8809{
8810 if (!::OpenClipboard(NULL))
8811 return;
8812 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
8813 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
8814 if (wbuf_handle == NULL)
8815 {
8816 ::CloseClipboard();
8817 return;
8818 }
8819 ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
8820 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
8821 ::GlobalUnlock(wbuf_handle);
8822 ::EmptyClipboard();
8823 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
8824 ::GlobalFree(wbuf_handle);
8825 ::CloseClipboard();
8826}
8827
8828#else
8829
8830// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
8831static const char* GetClipboardTextFn_DefaultImpl(void*)
8832{
8833 ImGuiContext& g = *GImGui;
8834 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
8835}
8836
8837// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
8838static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8839{
8840 ImGuiContext& g = *GImGui;
8841 g.PrivateClipboard.clear();
8842 const char* text_end = text + strlen(s: text);
8843 g.PrivateClipboard.resize(new_size: (int)(text_end - text) + 1);
8844 memcpy(dest: &g.PrivateClipboard[0], src: text, n: (size_t)(text_end - text));
8845 g.PrivateClipboard[(int)(text_end - text)] = 0;
8846}
8847
8848#endif
8849
8850// Win32 API IME support (for Asian languages, etc.)
8851#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
8852
8853#include <imm.h>
8854#ifdef _MSC_VER
8855#pragma comment(lib, "imm32")
8856#endif
8857
8858static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
8859{
8860 // Notify OS Input Method Editor of text input position
8861 if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
8862 if (HIMC himc = ::ImmGetContext(hwnd))
8863 {
8864 COMPOSITIONFORM cf;
8865 cf.ptCurrentPos.x = x;
8866 cf.ptCurrentPos.y = y;
8867 cf.dwStyle = CFS_FORCE_POSITION;
8868 ::ImmSetCompositionWindow(himc, &cf);
8869 ::ImmReleaseContext(hwnd, himc);
8870 }
8871}
8872
8873#else
8874
8875static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
8876
8877#endif
8878
8879//-----------------------------------------------------------------------------
8880// [SECTION] METRICS/DEBUG WINDOW
8881//-----------------------------------------------------------------------------
8882
8883void ImGui::ShowMetricsWindow(bool* p_open)
8884{
8885 if (!ImGui::Begin(name: "ImGui Metrics", p_open))
8886 {
8887 ImGui::End();
8888 return;
8889 }
8890 static bool show_draw_cmd_clip_rects = true;
8891 static bool show_window_begin_order = false;
8892 ImGuiIO& io = ImGui::GetIO();
8893 ImGui::Text(fmt: "Dear ImGui %s", ImGui::GetVersion());
8894 ImGui::Text(fmt: "Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
8895 ImGui::Text(fmt: "%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
8896 ImGui::Text(fmt: "%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
8897 ImGui::Text(fmt: "%d allocations", io.MetricsActiveAllocations);
8898 ImGui::Checkbox(label: "Show clipping rectangles when hovering draw commands", v: &show_draw_cmd_clip_rects);
8899 ImGui::Checkbox(label: "Ctrl shows window begin order", v: &show_window_begin_order);
8900
8901 ImGui::Separator();
8902
8903 struct Funcs
8904 {
8905 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
8906 {
8907 bool node_open = ImGui::TreeNode(ptr_id: draw_list, fmt: "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
8908 if (draw_list == ImGui::GetWindowDrawList())
8909 {
8910 ImGui::SameLine();
8911 ImGui::TextColored(col: ImColor(255,100,100), fmt: "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
8912 if (node_open) ImGui::TreePop();
8913 return;
8914 }
8915
8916 ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list
8917 if (window && IsItemHovered())
8918 overlay_draw_list->AddRect(a: window->Pos, b: window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
8919 if (!node_open)
8920 return;
8921
8922 int elem_offset = 0;
8923 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
8924 {
8925 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
8926 continue;
8927 if (pcmd->UserCallback)
8928 {
8929 ImGui::BulletText(fmt: "Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
8930 continue;
8931 }
8932 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
8933 bool pcmd_node_open = ImGui::TreeNode(ptr_id: (void*)(pcmd - draw_list->CmdBuffer.begin()), fmt: "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
8934 if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
8935 {
8936 ImRect clip_rect = pcmd->ClipRect;
8937 ImRect vtxs_rect;
8938 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
8939 vtxs_rect.Add(p: draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
8940 clip_rect.Floor(); overlay_draw_list->AddRect(a: clip_rect.Min, b: clip_rect.Max, IM_COL32(255,255,0,255));
8941 vtxs_rect.Floor(); overlay_draw_list->AddRect(a: vtxs_rect.Min, b: vtxs_rect.Max, IM_COL32(255,0,255,255));
8942 }
8943 if (!pcmd_node_open)
8944 continue;
8945
8946 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
8947 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
8948 while (clipper.Step())
8949 for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
8950 {
8951 char buf[300];
8952 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
8953 ImVec2 triangles_pos[3];
8954 for (int n = 0; n < 3; n++, vtx_i++)
8955 {
8956 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
8957 triangles_pos[n] = v.pos;
8958 buf_p += ImFormatString(buf: buf_p, buf_size: (int)(buf_end - buf_p), fmt: "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
8959 }
8960 ImGui::Selectable(label: buf, selected: false);
8961 if (ImGui::IsItemHovered())
8962 {
8963 ImDrawListFlags backup_flags = overlay_draw_list->Flags;
8964 overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
8965 overlay_draw_list->AddPolyline(points: triangles_pos, num_points: 3, IM_COL32(255,255,0,255), closed: true, thickness: 1.0f);
8966 overlay_draw_list->Flags = backup_flags;
8967 }
8968 }
8969 ImGui::TreePop();
8970 }
8971 ImGui::TreePop();
8972 }
8973
8974 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
8975 {
8976 if (!ImGui::TreeNode(str_id: label, fmt: "%s (%d)", label, windows.Size))
8977 return;
8978 for (int i = 0; i < windows.Size; i++)
8979 Funcs::NodeWindow(window: windows[i], label: "Window");
8980 ImGui::TreePop();
8981 }
8982
8983 static void NodeWindow(ImGuiWindow* window, const char* label)
8984 {
8985 if (!ImGui::TreeNode(ptr_id: window, fmt: "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
8986 return;
8987 ImGuiWindowFlags flags = window->Flags;
8988 NodeDrawList(window, draw_list: window->DrawList, label: "DrawList");
8989 ImGui::BulletText(fmt: "Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
8990 ImGui::BulletText(fmt: "Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
8991 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
8992 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
8993 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
8994 ImGui::BulletText(fmt: "Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetWindowScrollMaxX(window), window->Scroll.y, GetWindowScrollMaxY(window));
8995 ImGui::BulletText(fmt: "Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
8996 ImGui::BulletText(fmt: "Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
8997 ImGui::BulletText(fmt: "NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
8998 ImGui::BulletText(fmt: "NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
8999 if (!window->NavRectRel[0].IsInverted())
9000 ImGui::BulletText(fmt: "NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
9001 else
9002 ImGui::BulletText(fmt: "NavRectRel[0]: <None>");
9003 if (window->RootWindow != window) NodeWindow(window: window->RootWindow, label: "RootWindow");
9004 if (window->ParentWindow != NULL) NodeWindow(window: window->ParentWindow, label: "ParentWindow");
9005 if (window->DC.ChildWindows.Size > 0) NodeWindows(windows&: window->DC.ChildWindows, label: "ChildWindows");
9006 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode(str_id: "Columns", fmt: "Columns sets (%d)", window->ColumnsStorage.Size))
9007 {
9008 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9009 {
9010 const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
9011 if (ImGui::TreeNode(ptr_id: (void*)(uintptr_t)columns->ID, fmt: "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9012 {
9013 ImGui::BulletText(fmt: "Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
9014 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9015 ImGui::BulletText(fmt: "Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, offset_norm: columns->Columns[column_n].OffsetNorm));
9016 ImGui::TreePop();
9017 }
9018 }
9019 ImGui::TreePop();
9020 }
9021 ImGui::BulletText(fmt: "Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9022 ImGui::TreePop();
9023 }
9024 };
9025
9026 // Access private state, we are going to display the draw lists from last frame
9027 ImGuiContext& g = *GImGui;
9028 Funcs::NodeWindows(windows&: g.Windows, label: "Windows");
9029 if (ImGui::TreeNode(str_id: "DrawList", fmt: "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9030 {
9031 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9032 Funcs::NodeDrawList(NULL, draw_list: g.DrawDataBuilder.Layers[0][i], label: "DrawList");
9033 ImGui::TreePop();
9034 }
9035 if (ImGui::TreeNode(str_id: "Popups", fmt: "Popups (%d)", g.OpenPopupStack.Size))
9036 {
9037 for (int i = 0; i < g.OpenPopupStack.Size; i++)
9038 {
9039 ImGuiWindow* window = g.OpenPopupStack[i].Window;
9040 ImGui::BulletText(fmt: "PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9041 }
9042 ImGui::TreePop();
9043 }
9044 if (ImGui::TreeNode(label: "Internal state"))
9045 {
9046 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9047 ImGui::Text(fmt: "HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9048 ImGui::Text(fmt: "HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9049 ImGui::Text(fmt: "HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
9050 ImGui::Text(fmt: "ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
9051 ImGui::Text(fmt: "ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9052 ImGui::Text(fmt: "MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9053 ImGui::Text(fmt: "NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9054 ImGui::Text(fmt: "NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9055 ImGui::Text(fmt: "NavInputSource: %s", input_source_names[g.NavInputSource]);
9056 ImGui::Text(fmt: "NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9057 ImGui::Text(fmt: "NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9058 ImGui::Text(fmt: "NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9059 ImGui::Text(fmt: "NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9060 ImGui::Text(fmt: "DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9061 ImGui::TreePop();
9062 }
9063
9064
9065 if (g.IO.KeyCtrl && show_window_begin_order)
9066 {
9067 for (int n = 0; n < g.Windows.Size; n++)
9068 {
9069 ImGuiWindow* window = g.Windows[n];
9070 if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
9071 continue;
9072 char buf[32];
9073 ImFormatString(buf, IM_ARRAYSIZE(buf), fmt: "%d", window->BeginOrderWithinContext);
9074 float font_size = ImGui::GetFontSize() * 2;
9075 ImDrawList* overlay_draw_list = GetOverlayDrawList(window);
9076 overlay_draw_list->AddRectFilled(a: window->Pos, b: window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9077 overlay_draw_list->AddText(NULL, font_size, pos: window->Pos, IM_COL32(255, 255, 255, 255), text_begin: buf);
9078 }
9079 }
9080 ImGui::End();
9081}
9082
9083//-----------------------------------------------------------------------------
9084
9085// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9086// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
9087#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9088#include "imgui_user.inl"
9089#endif
9090
9091//-----------------------------------------------------------------------------
9092

source code of qt3d/src/3rdparty/imgui/imgui.cpp