1 // dear imgui, v1.65
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 
22 Index of this file:
23 
24 DOCUMENTATION
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 
50 CODE
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 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 
136  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
137 
138  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
139  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
140    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
141    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
142    likely be a comment about it. Please report any issue to the GitHub page!
143  - Try to keep your copy of dear imgui reasonably up to date.
144 
145  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
146 
147  - Run and study the examples and demo to get acquainted with the library.
148  - Add the Dear ImGui source files to your projects or using your preferred build system.
149    It is recommended you build the .cpp files as part of your project and not as a library.
150  - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
151  - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder.
152  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
153  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
154    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
155    phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
156  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
157 
158  THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE
159  EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder)
160 
161      // Application init: create a dear imgui context, setup some options, load fonts
162      ImGui::CreateContext();
163      ImGuiIO& io = ImGui::GetIO();
164      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls
165      // TODO: Fill optional fields of the io structure later.
166      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
167 
168      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
169      ImGui_ImplWin32_Init(hwnd);
170      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
171 
172      // Application main loop
173      while (true)
174      {
175          // Feed inputs to dear imgui, start new frame
176          ImGui_ImplDX11_NewFrame();
177          ImGui_ImplWin32_NewFrame();
178          ImGui::NewFrame();
179 
180          // Any application code here
181          ImGui::Text("Hello, world!");
182 
183          // Render dear imgui into screen
184          ImGui::Render();
185          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
186          g_pSwapChain->Present(1, 0);
187      }
188 
189      // Shutdown
190      ImGui_ImplDX11_Shutdown();
191      ImGui_ImplWin32_Shutdown();
192      ImGui::DestroyContext();
193 
194  THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE
195  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
196 
197      // Application init: create a dear imgui context, setup some options, load fonts
198      ImGui::CreateContext();
199      ImGuiIO& io = ImGui::GetIO();
200      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls
201      // TODO: Fill optional fields of the io structure later.
202      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
203 
204      // Build and load the texture atlas into a texture
205      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
206      int width, height;
207      unsigned char* pixels = NULL;
208      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
209 
210      // At this point you've got the texture data and you need to upload that your your graphic system:
211      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
212      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
213      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
214      io.Fonts->TexID = (void*)texture;
215 
216      // Application main loop
217      while (true)
218      {
219         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
220         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
221         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
222         io.DisplaySize.x = 1920.0f;             // set the current display width
223         io.DisplaySize.y = 1280.0f;             // set the current display height here
224         io.MousePos = my_mouse_pos;             // set the mouse position
225         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
226         io.MouseDown[1] = my_mouse_buttons[1];
227 
228         // Call NewFrame(), after this point you can use ImGui::* functions anytime
229         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
230         ImGui::NewFrame();
231 
232         // Most of your application code here
233         ImGui::Text("Hello, world!");
234         MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
235         MyGameRender(); // may use any ImGui functions as well!
236 
237         // Render imgui, swap buffers
238         // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
239         ImGui::EndFrame();
240         ImGui::Render();
241         ImDrawData* draw_data = ImGui::GetDrawData();
242         MyImGuiRenderFunction(draw_data);
243         SwapBuffers();
244      }
245 
246      // Shutdown
247      ImGui::DestroyContext();
248 
249  THIS HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
250 
251     void void MyImGuiRenderFunction(ImDrawData* draw_data)
252     {
253        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
254        // TODO: Setup viewport using draw_data->DisplaySize
255        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
256        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
257        for (int n = 0; n < draw_data->CmdListsCount; n++)
258        {
259           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by ImGui
260           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by ImGui
261           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
262           {
263              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
264              if (pcmd->UserCallback)
265              {
266                  pcmd->UserCallback(cmd_list, pcmd);
267              }
268              else
269              {
270                  // The texture for the draw call is specified by pcmd->TextureId.
271                  // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
272                  MyEngineBindTexture(pcmd->TextureId);
273 
274                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
275                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
276                  //   (some elements visible outside their bounds) but you can fix that once everywhere else works!
277                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
278                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
279                  //   However, in the interest of supporting multi-viewport applications in the future, always subtract draw_data->DisplayPos from
280                  //   clipping bounds to convert them to your viewport space.
281                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
282                  ImVec2 pos = draw_data->DisplayPos;
283                  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));
284 
285                  // Render 'pcmd->ElemCount/3' indexed triangles.
286                  // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
287                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
288              }
289              idx_buffer += pcmd->ElemCount;
290           }
291        }
292     }
293 
294  - The examples/ folders contains many functional implementation of the pseudo-code above.
295  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
296    They tell you if ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application.
297    In both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags.
298  - Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues!
299 
300  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
301 
302  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
303  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
304  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
305  - Gamepad:
306     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
307     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
308       Note that io.NavInputs[] is cleared by EndFrame().
309     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
310          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
311     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
312       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.).
313     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
314     - 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
315       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
316  - Keyboard:
317     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
318       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
319     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
320       will be set. For more advanced uses, you may want to read from:
321        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
322        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
323        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
324       Please reach out if you think the game vs navigation input sharing could be improved.
325  - Mouse:
326     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
327     - 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.
328     - 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.
329       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
330       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.
331       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.
332       (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!)
333       (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
334        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
335 
336 
337  API BREAKING CHANGES
338  ====================
339 
340  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
341  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.
342  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.
343  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
344 
345  - 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.
346                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
347  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
348  - 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.
349                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
350                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
351  - 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).
352  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
353  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
354  - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature.
355  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
356  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
357  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
358  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
359  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
360  - 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.
361                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
362                        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.
363                        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.
364  - 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",
365                        consistent with other functions. Kept redirection functions (will obsolete).
366  - 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.
367  - 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).
368  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
369  - 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.
370  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
371  - 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.
372  - 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.
373  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
374                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
375                        - removed Shutdown() function, as DestroyContext() serve this purpose.
376                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
377                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
378                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
379  - 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.
380  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
381  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
382  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
383  - 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.
384  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
385  - 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
386  - 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.
387  - 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.
388  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
389  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
390                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
391  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
392  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
393  - 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.
394  - 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.
395                        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.
396  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
397  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
398  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
399  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
400  - 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.
401  - 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.
402  - 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.
403                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
404  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
405  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
406  - 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).
407  - 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)".
408  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
409                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
410                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
411  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
412  - 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.
413  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
414  - 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.
415  - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
416  - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
417  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
418  - 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.
419                      - 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.
420                      - 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))'
421  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
422  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
423  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
424  - 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().
425  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
426  - 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.
427  - 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.
428  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
429                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
430                        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.
431                        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.
432                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
433                            {
434                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
435                                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);
436                            }
437                        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.
438  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
439  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
440  - 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).
441  - 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.
442  - 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).
443  - 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)
444  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
445  - 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.
446  - 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.
447  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
448  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
449  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
450                        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.
451                        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!
452  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
453  - 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.
454  - 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
455  - 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.
456                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
457  - 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.
458                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
459                      - 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.
460                      - the signature of the io.RenderDrawListsFn handler has changed!
461                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
462                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
463                          argument:   'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
464                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
465                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
466                      - 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.
467                      - 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!
468                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
469  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
470  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
471  - 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.
472  - 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
473  - 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!
474  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
475  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
476  - 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.
477  - 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.
478  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
479  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
480  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
481  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
482  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
483  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
484  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
485  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
486  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
487  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
488  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
489  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
490  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
491  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
492  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
493  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
494  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
495               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
496                        font init:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>
497                        became:     unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier;
498                        you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
499                        it is now recommended that you sample the font texture with bilinear interpolation.
500               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
501               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
502               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
503  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
504  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
505  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
506  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
507  - 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)
508  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
509  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
510  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
511  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
512  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
513  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
514 
515 
516  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
517  ======================================
518 
519  Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
520  A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
521     - 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.
522     - 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.
523     - 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).
524     Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
525      This is because imgui needs to detect that you clicked in the void to unfocus its windows.
526     Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
527      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.
528      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
529      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().
530     Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
531      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
532      were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
533 
534  Q: How can I display an image? What is ImTextureID, how does it works?
535  A: Short explanation:
536     - 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.
537     - Actual textures are identified in a way that is up to the user/engine.
538     - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
539       Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
540 
541     Long explanation:
542     - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
543       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
544       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.).
545     - 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.
546       We carry the information to identify a "texture" in the ImTextureID type.
547       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.
548       Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
549     - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
550       an image from the end-user perspective. This is what the _examples_ rendering functions are using:
551 
552          OpenGL:     ImTextureID = GLuint                       (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
553          DirectX9:   ImTextureID = LPDIRECT3DTEXTURE9           (see ImGui_ImplDX9_RenderDrawData()     function in imgui_impl_dx9.cpp)
554          DirectX11:  ImTextureID = ID3D11ShaderResourceView*    (see ImGui_ImplDX11_RenderDrawData()    function in imgui_impl_dx11.cpp)
555          DirectX12:  ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE  (see ImGui_ImplDX12_RenderDrawData()    function in imgui_impl_dx12.cpp)
556 
557       For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
558       Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
559       tying together both the texture and information about its format and how to read it.
560     - 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
561       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
562       is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
563       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
564       representation suggested by the example bindings is probably the best choice.
565       (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
566 
567     User code may do:
568 
569         // Cast our texture type to ImTextureID / void*
570         MyTexture* texture = g_CoffeeTableTexture;
571         ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
572 
573     The renderer function called after ImGui::Render() will receive that same value that the user code passed:
574 
575         // Cast ImTextureID / void* stored in the draw command as our texture type
576         MyTexture* texture = (MyTexture*)pcmd->TextureId;
577         MyEngineBindTexture2D(texture);
578 
579     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.
580     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.
581     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.
582 
583     Here's a simplified OpenGL example using stb_image.h:
584 
585         // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
586         #define STB_IMAGE_IMPLEMENTATION
587         #include <stb_image.h>
588         [...]
589         int my_image_width, my_image_height;
590         unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
591 
592         // Turn the RGBA pixel data into an OpenGL texture:
593         GLuint my_opengl_texture;
594         glGenTextures(1, &my_opengl_texture);
595         glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
596         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
597         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
598 
599         // 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:
600         ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
601 
602     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.
603     Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
604     Examples:
605 
606         GLuint my_tex = XXX;
607         void* my_void_ptr;
608         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)
609         my_tex = (GLuint)(intptr_t)my_void_ptr;                 // cast a void* into a GLuint
610 
611         ID3D11ShaderResourceView* my_dx11_srv = XXX;
612         void* my_void_ptr;
613         my_void_ptr = (void*)my_dx11_srv;                       // cast a ID3D11ShaderResourceView* into an opaque void*
614         my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr;   // cast a void* into a ID3D11ShaderResourceView*
615 
616     Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
617 
618  Q: How can I have multiple widgets with the same label or without a label?
619  Q: I have multiple widgets with the same label, and only the first one works. Why is that?
620  A: A primer on labels and the ID Stack...
621 
622     Dear ImGui internally need to uniquely identify UI elements.
623     Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
624     Interactive widgets (such as calls to Button buttons) need a unique ID.
625     Unique ID are used internally to track active widgets and occasionally associate state to widgets.
626     Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
627 
628    - Unique ID are often derived from a string label:
629 
630        Button("OK");          // Label = "OK",     ID = hash of (..., "OK")
631        Button("Cancel");      // Label = "Cancel", ID = hash of (..., "Cancel")
632 
633    - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
634      two buttons labeled "OK" in different windows or different tree locations is fine.
635      We used "..." above to signify whatever was already pushed to the ID stack previously:
636 
637        Begin("MyWindow");
638        Button("OK");          // Label = "OK",     ID = hash of ("MyWindow", "OK")
639        End();
640 
641    - If you have a same ID twice in the same location, you'll have a conflict:
642 
643        Button("OK");
644        Button("OK");          // ID collision! Interacting with either button will trigger the first one.
645 
646      Fear not! this is easy to solve and there are many ways to solve it!
647 
648    - Solving ID conflict in a simple/local context:
649      When passing a label you can optionally specify extra ID information within string itself.
650      Use "##" to pass a complement to the ID that won't be visible to the end-user.
651      This helps solving the simple collision cases when you know e.g. at compilation time which items
652      are going to be created:
653 
654        Begin("MyWindow");
655        Button("Play");        // Label = "Play",   ID = hash of ("MyWindow", "Play")
656        Button("Play##foo1");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo1")  // Different from above
657        Button("Play##foo2");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo2")  // Different from above
658        End();
659 
660    - If you want to completely hide the label, but still need an ID:
661 
662        Checkbox("##On", &b);  // Label = "",       ID = hash of (..., "##On")   // No visible label!
663 
664    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
665      you to animate labels. For example you may want to include varying information in a window title bar,
666      but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
667 
668        Button("Hello###ID";   // Label = "Hello",  ID = hash of (..., "ID")
669        Button("World###ID";   // Label = "World",  ID = hash of (..., "ID")     // Same as above, even though the label looks different
670 
671        sprintf(buf, "My game (%f FPS)###MyGame", fps);
672        Begin(buf);            // Variable label,   ID = hash of "MyGame"
673 
674    - Solving ID conflict in a more general manner:
675      Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
676      within the same window. This is the most convenient way of distinguishing ID when iterating and
677      creating many UI elements programmatically.
678      You can push a pointer, a string or an integer value into the ID stack.
679      Remember that ID are formed from the concatenation of _everything_ in the ID stack!
680 
681        Begin("Window");
682        for (int i = 0; i < 100; i++)
683        {
684          PushID(i);         // Push i to the id tack
685          Button("Click");   // Label = "Click",  ID = Hash of ("Window", i, "Click")
686          PopID();
687        }
688        for (int i = 0; i < 100; i++)
689        {
690          MyObject* obj = Objects[i];
691          PushID(obj);
692          Button("Click");   // Label = "Click",  ID = Hash of ("Window", obj pointer, "Click")
693          PopID();
694        }
695        for (int i = 0; i < 100; i++)
696        {
697          MyObject* obj = Objects[i];
698          PushID(obj->Name);
699          Button("Click");   // Label = "Click",  ID = Hash of ("Window", obj->Name, "Click")
700          PopID();
701        }
702        End();
703 
704    - More example showing that you can stack multiple prefixes into the ID stack:
705 
706        Button("Click");     // Label = "Click",  ID = hash of (..., "Click")
707        PushID("node");
708        Button("Click");     // Label = "Click",  ID = hash of (..., "node", "Click")
709          PushID(my_ptr);
710            Button("Click"); // Label = "Click",  ID = hash of (..., "node", my_ptr, "Click")
711          PopID();
712        PopID();
713 
714    - Tree nodes implicitly creates a scope for you by calling PushID().
715 
716        Button("Click");     // Label = "Click",  ID = hash of (..., "Click")
717        if (TreeNode("node"))
718        {
719          Button("Click");   // Label = "Click",  ID = hash of (..., "node", "Click")
720          TreePop();
721        }
722 
723    - When working with trees, ID are used to preserve the open/close state of each tree node.
724      Depending on your use cases you may want to use strings, indices or pointers as ID.
725       e.g. when following a single pointer that may change over time, using a static string as ID
726        will preserve your node open/closed state when the targeted object change.
727       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
728        node open/closed state differently. See what makes more sense in your situation!
729 
730  Q: How can I use my own math types instead of ImVec2/ImVec4?
731  A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
732     This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
733 
734  Q: How can I load a different font than the default?
735  A: Use the font atlas to load the TTF/OTF file you want:
736       ImGuiIO& io = ImGui::GetIO();
737       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
738       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
739     (default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code)
740 
741     New programmers: remember that in C/C++ and most programming languages if you want to use a
742     backslash \ within a string literal, you need to write it double backslash "\\":
743       io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels);   // WRONG (you are escape the M here!)
744       io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels);  // CORRECT
745       io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels);   // ALSO CORRECT
746 
747  Q: How can I easily use icons in my application?
748  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
749     main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?'
750     and the file 'misc/fonts/README.txt' for instructions and useful header files.
751 
752  Q: How can I load multiple fonts?
753  A: Use the font atlas to pack them into a single texture:
754     (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.)
755 
756       ImGuiIO& io = ImGui::GetIO();
757       ImFont* font0 = io.Fonts->AddFontDefault();
758       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
759       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
760       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
761       // the first loaded font gets used by default
762       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
763 
764       // Options
765       ImFontConfig config;
766       config.OversampleH = 3;
767       config.OversampleV = 1;
768       config.GlyphOffset.y -= 2.0f;      // Move everything by 2 pixels up
769       config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
770       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
771 
772       // Combine multiple fonts into one (e.g. for icon fonts)
773       ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
774       ImFontConfig config;
775       config.MergeMode = true;
776       io.Fonts->AddFontDefault();
777       io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
778       io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
779 
780  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
781  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
782 
783       // Add default Japanese ranges
784       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
785 
786       // 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)
787       ImVector<ImWchar> ranges;
788       ImFontAtlas::GlyphRangesBuilder builder;
789       builder.AddText("Hello world");                        // Add a string (here "Hello world" contains 7 unique characters)
790       builder.AddChar(0x7262);                               // Add a specific character
791       builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
792       builder.BuildRanges(&ranges);                          // Build the final result (ordered ranges with all the unique characters submitted)
793       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
794 
795     All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
796     by using the u8"hello" syntax. Specifying literal in your source code using a local code page
797     (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
798     Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
799 
800     Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
801     The applications in examples/ are doing that.
802     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).
803     You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
804     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
805     the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
806 
807  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
808  A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags.
809       Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
810     - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
811     - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData.
812 
813  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
814  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
815     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
816 
817  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
818  A: You are probably mishandling the clipping rectangles in your render function.
819     Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
820 
821  Q: How can I help?
822  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!
823     - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README.
824     - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
825       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers.
826       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
827     - 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).
828 
829  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
830         this is also useful to set yourself in the context of another window (to get/set other settings)
831  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
832  - 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
833         of a deep nested inner loop in your code.
834  - tip: you can call Render() multiple times (e.g for VR renders).
835  - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
836 
837 */
838 
839 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
840 #define _CRT_SECURE_NO_WARNINGS
841 #endif
842 
843 #include "imgui.h"
844 #ifndef IMGUI_DEFINE_MATH_OPERATORS
845 #define IMGUI_DEFINE_MATH_OPERATORS
846 #endif
847 #include "imgui_internal.h"
848 
849 #include <ctype.h>      // toupper, isprint
850 #include <stdio.h>      // vsnprintf, sscanf, printf
851 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
852 #include <stddef.h>     // intptr_t
853 #else
854 #include <stdint.h>     // intptr_t
855 #endif
856 
857 #define IMGUI_DEBUG_NAV_SCORING     0
858 #define IMGUI_DEBUG_NAV_RECTS       0
859 
860 // Visual Studio warnings
861 #ifdef _MSC_VER
862 #pragma warning (disable: 4127) // condition expression is constant
863 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
864 #endif
865 
866 // Clang/GCC warnings with -Weverything
867 #ifdef __clang__
868 #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!
869 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
870 #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.
871 #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.
872 #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.
873 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
874 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
875 #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.
876 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
877 #elif defined(__GNUC__)
878 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
879 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
880 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
881 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
882 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
883 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
884 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
885 #if __GNUC__ >= 8
886 #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
887 #endif
888 #endif
889 
890 // 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.
891 static const float NAV_WINDOWING_HIGHLIGHT_DELAY   = 0.20f; // Time before the highlight and screen dimming starts fading in
892 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
893 
894 //-------------------------------------------------------------------------
895 // [SECTION] FORWARD DECLARATIONS
896 //-------------------------------------------------------------------------
897 
898 static void             SetCurrentWindow(ImGuiWindow* window);
899 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
900 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
901 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
902 static void             FindHoveredWindow();
903 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
904 static void             CheckStacksSize(ImGuiWindow* window, bool write);
905 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
906 
907 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
908 static void             AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
909 static void             AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
910 
911 static ImRect           GetViewportRect();
912 
913 // Settings
914 static void*            SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
915 static void             SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
916 static void             SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
917 
918 // Platform Dependents default implementation for IO functions
919 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
920 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
921 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
922 
923 namespace ImGui
924 {
925 static bool             BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
926 
927 // Navigation
928 static void             NavUpdate();
929 static void             NavUpdateWindowing();
930 static void             NavUpdateWindowingList();
931 static void             NavUpdateMoveResult();
932 static float            NavUpdatePageUpPageDown(int allowed_dir_flags);
933 static inline void      NavUpdateAnyRequestFlag();
934 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
935 static ImVec2           NavCalcPreferredRefPos();
936 static void             NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
937 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
938 
939 // Misc
940 static void             UpdateMouseInputs();
941 static void             UpdateMouseWheel();
942 static void             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
943 }
944 
945 //-----------------------------------------------------------------------------
946 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
947 //-----------------------------------------------------------------------------
948 
949 // Current context pointer. Implicitly used by all ImGui functions. Always assumed to be != NULL.
950 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
951 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
952 // 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:
953 // - 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
954 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
955 #ifndef GImGui
956 ImGuiContext*   GImGui = NULL;
957 #endif
958 
959 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
960 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
961 // 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.
962 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)963 static void*   MallocWrapper(size_t size, void* user_data)    { (void)user_data; return malloc(size); }
FreeWrapper(void * ptr,void * user_data)964 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; free(ptr); }
965 #else
MallocWrapper(size_t size,void * user_data)966 static void*   MallocWrapper(size_t size, void* user_data)    { (void)user_data; (void)size; IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)967 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; (void)ptr; IM_ASSERT(0); }
968 #endif
969 
970 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
971 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
972 static void*    GImAllocatorUserData = NULL;
973 
974 //-----------------------------------------------------------------------------
975 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
976 //-----------------------------------------------------------------------------
977 
ImGuiStyle()978 ImGuiStyle::ImGuiStyle()
979 {
980     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
981     WindowPadding           = ImVec2(8,8);      // Padding within a window
982     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
983     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
984     WindowMinSize           = ImVec2(32,32);    // Minimum window size
985     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
986     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
987     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
988     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
989     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
990     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
991     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
992     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
993     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
994     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
995     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!
996     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
997     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
998     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
999     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
1000     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
1001     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1002     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1003     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.
1004     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.
1005     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1006     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1007     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1008     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.
1009 
1010     // Default theme
1011     ImGui::StyleColorsDark(this);
1012 }
1013 
1014 // 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.
1015 // 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.
ScaleAllSizes(float scale_factor)1016 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1017 {
1018     WindowPadding = ImFloor(WindowPadding * scale_factor);
1019     WindowRounding = ImFloor(WindowRounding * scale_factor);
1020     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1021     ChildRounding = ImFloor(ChildRounding * scale_factor);
1022     PopupRounding = ImFloor(PopupRounding * scale_factor);
1023     FramePadding = ImFloor(FramePadding * scale_factor);
1024     FrameRounding = ImFloor(FrameRounding * scale_factor);
1025     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1026     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1027     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1028     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1029     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1030     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1031     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1032     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1033     GrabRounding = ImFloor(GrabRounding * scale_factor);
1034     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1035     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1036     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1037 }
1038 
ImGuiIO()1039 ImGuiIO::ImGuiIO()
1040 {
1041     // Most fields are initialized with zero
1042     memset(this, 0, sizeof(*this));
1043 
1044     // Settings
1045     ConfigFlags = 0x00;
1046     BackendFlags = 0x00;
1047     DisplaySize = ImVec2(-1.0f, -1.0f);
1048     DeltaTime = 1.0f/60.0f;
1049     IniSavingRate = 5.0f;
1050     IniFilename = "imgui.ini";
1051     LogFilename = "imgui_log.txt";
1052     MouseDoubleClickTime = 0.30f;
1053     MouseDoubleClickMaxDist = 6.0f;
1054     for (int i = 0; i < ImGuiKey_COUNT; i++)
1055         KeyMap[i] = -1;
1056     KeyRepeatDelay = 0.250f;
1057     KeyRepeatRate = 0.050f;
1058     UserData = NULL;
1059 
1060     Fonts = NULL;
1061     FontGlobalScale = 1.0f;
1062     FontDefault = NULL;
1063     FontAllowUserScaling = false;
1064     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1065     DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
1066 
1067     // Miscellaneous configuration options
1068 #ifdef __APPLE__
1069     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1070 #else
1071     ConfigMacOSXBehaviors = false;
1072 #endif
1073     ConfigInputTextCursorBlink = true;
1074     ConfigResizeWindowsFromEdges = false;
1075 
1076     // Settings (User Functions)
1077     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1078     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1079     ClipboardUserData = NULL;
1080     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1081     ImeWindowHandle = NULL;
1082 
1083 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1084     RenderDrawListsFn = NULL;
1085 #endif
1086 
1087     // Input (NB: we already have memset zero the entire structure)
1088     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1089     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1090     MouseDragThreshold = 6.0f;
1091     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1092     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1093     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1094 }
1095 
1096 // Pass in translated ASCII characters for text input.
1097 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1098 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)1099 void ImGuiIO::AddInputCharacter(ImWchar c)
1100 {
1101     const int n = ImStrlenW(InputCharacters);
1102     if (n + 1 < IM_ARRAYSIZE(InputCharacters))
1103     {
1104         InputCharacters[n] = c;
1105         InputCharacters[n+1] = '\0';
1106     }
1107 }
1108 
AddInputCharactersUTF8(const char * utf8_chars)1109 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1110 {
1111     // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
1112     const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
1113     ImWchar wchars[wchars_buf_len];
1114     ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
1115     for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
1116         AddInputCharacter(wchars[i]);
1117 }
1118 
1119 //-----------------------------------------------------------------------------
1120 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1121 //-----------------------------------------------------------------------------
1122 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1123 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1124 {
1125     ImVec2 ap = p - a;
1126     ImVec2 ab_dir = b - a;
1127     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1128     if (dot < 0.0f)
1129         return a;
1130     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1131     if (dot > ab_len_sqr)
1132         return b;
1133     return a + ab_dir * dot / ab_len_sqr;
1134 }
1135 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1136 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1137 {
1138     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1139     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1140     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1141     return ((b1 == b2) && (b2 == b3));
1142 }
1143 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1144 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1145 {
1146     ImVec2 v0 = b - a;
1147     ImVec2 v1 = c - a;
1148     ImVec2 v2 = p - a;
1149     const float denom = v0.x * v1.y - v1.x * v0.y;
1150     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1151     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1152     out_u = 1.0f - out_v - out_w;
1153 }
1154 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1155 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1156 {
1157     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1158     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1159     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1160     float dist2_ab = ImLengthSqr(p - proj_ab);
1161     float dist2_bc = ImLengthSqr(p - proj_bc);
1162     float dist2_ca = ImLengthSqr(p - proj_ca);
1163     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1164     if (m == dist2_ab)
1165         return proj_ab;
1166     if (m == dist2_bc)
1167         return proj_bc;
1168     return proj_ca;
1169 }
1170 
ImStricmp(const char * str1,const char * str2)1171 int ImStricmp(const char* str1, const char* str2)
1172 {
1173     int d;
1174     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1175     return d;
1176 }
1177 
ImStrnicmp(const char * str1,const char * str2,size_t count)1178 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1179 {
1180     int d = 0;
1181     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1182     return d;
1183 }
1184 
ImStrncpy(char * dst,const char * src,size_t count)1185 void ImStrncpy(char* dst, const char* src, size_t count)
1186 {
1187     if (count < 1) return;
1188     strncpy(dst, src, count);
1189     dst[count-1] = 0;
1190 }
1191 
ImStrdup(const char * str)1192 char* ImStrdup(const char *str)
1193 {
1194     size_t len = strlen(str) + 1;
1195     void* buf = ImGui::MemAlloc(len);
1196     return (char*)memcpy(buf, (const void*)str, len);
1197 }
1198 
ImStrchrRange(const char * str,const char * str_end,char c)1199 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1200 {
1201     for ( ; str < str_end; str++)
1202         if (*str == c)
1203             return str;
1204     return NULL;
1205 }
1206 
ImStrlenW(const ImWchar * str)1207 int ImStrlenW(const ImWchar* str)
1208 {
1209     int n = 0;
1210     while (*str++) n++;
1211     return n;
1212 }
1213 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1214 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1215 {
1216     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1217         buf_mid_line--;
1218     return buf_mid_line;
1219 }
1220 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1221 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1222 {
1223     if (!needle_end)
1224         needle_end = needle + strlen(needle);
1225 
1226     const char un0 = (char)toupper(*needle);
1227     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1228     {
1229         if (toupper(*haystack) == un0)
1230         {
1231             const char* b = needle + 1;
1232             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1233                 if (toupper(*a) != toupper(*b))
1234                     break;
1235             if (b == needle_end)
1236                 return haystack;
1237         }
1238         haystack++;
1239     }
1240     return NULL;
1241 }
1242 
1243 // 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.
ImStrTrimBlanks(char * buf)1244 void ImStrTrimBlanks(char* buf)
1245 {
1246     char* p = buf;
1247     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1248         p++;
1249     char* p_start = p;
1250     while (*p != 0)                         // Find end of string
1251         p++;
1252     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1253         p--;
1254     if (p_start != buf)                     // Copy memory if we had leading blanks
1255         memmove(buf, p_start, p - p_start);
1256     buf[p - p_start] = 0;                   // Zero terminate
1257 }
1258 
1259 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1260 // 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.
1261 // B) When buf==NULL vsnprintf() will return the output size.
1262 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1263 
1264 #if defined(_MSC_VER) && !defined(vsnprintf)
1265 #define vsnprintf _vsnprintf
1266 #endif
1267 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1268 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1269 {
1270     va_list args;
1271     va_start(args, fmt);
1272     int w = vsnprintf(buf, buf_size, fmt, args);
1273     va_end(args);
1274     if (buf == NULL)
1275         return w;
1276     if (w == -1 || w >= (int)buf_size)
1277         w = (int)buf_size - 1;
1278     buf[w] = 0;
1279     return w;
1280 }
1281 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1282 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1283 {
1284     int w = vsnprintf(buf, buf_size, fmt, args);
1285     if (buf == NULL)
1286         return w;
1287     if (w == -1 || w >= (int)buf_size)
1288         w = (int)buf_size - 1;
1289     buf[w] = 0;
1290     return w;
1291 }
1292 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1293 
1294 // Pass data_size==0 for zero-terminated strings
1295 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHash(const void * data,int data_size,ImU32 seed)1296 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1297 {
1298     static ImU32 crc32_lut[256] = { 0 };
1299     if (!crc32_lut[1])
1300     {
1301         const ImU32 polynomial = 0xEDB88320;
1302         for (ImU32 i = 0; i < 256; i++)
1303         {
1304             ImU32 crc = i;
1305             for (ImU32 j = 0; j < 8; j++)
1306                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1307             crc32_lut[i] = crc;
1308         }
1309     }
1310 
1311     seed = ~seed;
1312     ImU32 crc = seed;
1313     const unsigned char* current = (const unsigned char*)data;
1314 
1315     if (data_size > 0)
1316     {
1317         // Known size
1318         while (data_size--)
1319             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1320     }
1321     else
1322     {
1323         // Zero-terminated string
1324         while (unsigned char c = *current++)
1325         {
1326             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1327             // Because this syntax is rarely used we are optimizing for the common case.
1328             // - If we reach ### in the string we discard the hash so far and reset to the seed.
1329             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1330             if (c == '#' && current[0] == '#' && current[1] == '#')
1331                 crc = seed;
1332             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1333         }
1334     }
1335     return ~crc;
1336 }
1337 
ImFileOpen(const char * filename,const char * mode)1338 FILE* ImFileOpen(const char* filename, const char* mode)
1339 {
1340 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1341     // 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)
1342     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1343     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1344     ImVector<ImWchar> buf;
1345     buf.resize(filename_wsize + mode_wsize);
1346     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1347     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1348     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1349 #else
1350     return fopen(filename, mode);
1351 #endif
1352 }
1353 
1354 // Load file content into memory
1355 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,size_t * out_file_size,int padding_bytes)1356 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1357 {
1358     IM_ASSERT(filename && file_open_mode);
1359     if (out_file_size)
1360         *out_file_size = 0;
1361 
1362     FILE* f;
1363     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1364         return NULL;
1365 
1366     long file_size_signed;
1367     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1368     {
1369         fclose(f);
1370         return NULL;
1371     }
1372 
1373     size_t file_size = (size_t)file_size_signed;
1374     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1375     if (file_data == NULL)
1376     {
1377         fclose(f);
1378         return NULL;
1379     }
1380     if (fread(file_data, 1, file_size, f) != file_size)
1381     {
1382         fclose(f);
1383         ImGui::MemFree(file_data);
1384         return NULL;
1385     }
1386     if (padding_bytes > 0)
1387         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1388 
1389     fclose(f);
1390     if (out_file_size)
1391         *out_file_size = file_size;
1392 
1393     return file_data;
1394 }
1395 
1396 //-----------------------------------------------------------------------------
1397 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1398 //-----------------------------------------------------------------------------
1399 
1400 // Convert UTF-8 to 32-bits character, process single character input.
1401 // Based on stb_from_utf8() from github.com/nothings/stb/
1402 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1403 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1404 {
1405     unsigned int c = (unsigned int)-1;
1406     const unsigned char* str = (const unsigned char*)in_text;
1407     if (!(*str & 0x80))
1408     {
1409         c = (unsigned int)(*str++);
1410         *out_char = c;
1411         return 1;
1412     }
1413     if ((*str & 0xe0) == 0xc0)
1414     {
1415         *out_char = 0xFFFD; // will be invalid but not end of string
1416         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1417         if (*str < 0xc2) return 2;
1418         c = (unsigned int)((*str++ & 0x1f) << 6);
1419         if ((*str & 0xc0) != 0x80) return 2;
1420         c += (*str++ & 0x3f);
1421         *out_char = c;
1422         return 2;
1423     }
1424     if ((*str & 0xf0) == 0xe0)
1425     {
1426         *out_char = 0xFFFD; // will be invalid but not end of string
1427         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1428         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1429         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1430         c = (unsigned int)((*str++ & 0x0f) << 12);
1431         if ((*str & 0xc0) != 0x80) return 3;
1432         c += (unsigned int)((*str++ & 0x3f) << 6);
1433         if ((*str & 0xc0) != 0x80) return 3;
1434         c += (*str++ & 0x3f);
1435         *out_char = c;
1436         return 3;
1437     }
1438     if ((*str & 0xf8) == 0xf0)
1439     {
1440         *out_char = 0xFFFD; // will be invalid but not end of string
1441         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1442         if (*str > 0xf4) return 4;
1443         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1444         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1445         c = (unsigned int)((*str++ & 0x07) << 18);
1446         if ((*str & 0xc0) != 0x80) return 4;
1447         c += (unsigned int)((*str++ & 0x3f) << 12);
1448         if ((*str & 0xc0) != 0x80) return 4;
1449         c += (unsigned int)((*str++ & 0x3f) << 6);
1450         if ((*str & 0xc0) != 0x80) return 4;
1451         c += (*str++ & 0x3f);
1452         // utf-8 encodings of values used in surrogate pairs are invalid
1453         if ((c & 0xFFFFF800) == 0xD800) return 4;
1454         *out_char = c;
1455         return 4;
1456     }
1457     *out_char = 0;
1458     return 0;
1459 }
1460 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1461 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1462 {
1463     ImWchar* buf_out = buf;
1464     ImWchar* buf_end = buf + buf_size;
1465     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1466     {
1467         unsigned int c;
1468         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1469         if (c == 0)
1470             break;
1471         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1472             *buf_out++ = (ImWchar)c;
1473     }
1474     *buf_out = 0;
1475     if (in_text_remaining)
1476         *in_text_remaining = in_text;
1477     return (int)(buf_out - buf);
1478 }
1479 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1480 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1481 {
1482     int char_count = 0;
1483     while ((!in_text_end || in_text < in_text_end) && *in_text)
1484     {
1485         unsigned int c;
1486         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1487         if (c == 0)
1488             break;
1489         if (c < 0x10000)
1490             char_count++;
1491     }
1492     return char_count;
1493 }
1494 
1495 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1496 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1497 {
1498     if (c < 0x80)
1499     {
1500         buf[0] = (char)c;
1501         return 1;
1502     }
1503     if (c < 0x800)
1504     {
1505         if (buf_size < 2) return 0;
1506         buf[0] = (char)(0xc0 + (c >> 6));
1507         buf[1] = (char)(0x80 + (c & 0x3f));
1508         return 2;
1509     }
1510     if (c >= 0xdc00 && c < 0xe000)
1511     {
1512         return 0;
1513     }
1514     if (c >= 0xd800 && c < 0xdc00)
1515     {
1516         if (buf_size < 4) return 0;
1517         buf[0] = (char)(0xf0 + (c >> 18));
1518         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1519         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1520         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1521         return 4;
1522     }
1523     //else if (c < 0x10000)
1524     {
1525         if (buf_size < 3) return 0;
1526         buf[0] = (char)(0xe0 + (c >> 12));
1527         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1528         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1529         return 3;
1530     }
1531 }
1532 
1533 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1534 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1535 {
1536     unsigned int dummy = 0;
1537     return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1538 }
1539 
ImTextCountUtf8BytesFromChar(unsigned int c)1540 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1541 {
1542     if (c < 0x80) return 1;
1543     if (c < 0x800) return 2;
1544     if (c >= 0xdc00 && c < 0xe000) return 0;
1545     if (c >= 0xd800 && c < 0xdc00) return 4;
1546     return 3;
1547 }
1548 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1549 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1550 {
1551     char* buf_out = buf;
1552     const char* buf_end = buf + buf_size;
1553     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1554     {
1555         unsigned int c = (unsigned int)(*in_text++);
1556         if (c < 0x80)
1557             *buf_out++ = (char)c;
1558         else
1559             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1560     }
1561     *buf_out = 0;
1562     return (int)(buf_out - buf);
1563 }
1564 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1565 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1566 {
1567     int bytes_count = 0;
1568     while ((!in_text_end || in_text < in_text_end) && *in_text)
1569     {
1570         unsigned int c = (unsigned int)(*in_text++);
1571         if (c < 0x80)
1572             bytes_count++;
1573         else
1574             bytes_count += ImTextCountUtf8BytesFromChar(c);
1575     }
1576     return bytes_count;
1577 }
1578 
1579 //-----------------------------------------------------------------------------
1580 // [SECTION] MISC HELPER/UTILTIES (Color functions)
1581 // Note: The Convert functions are early design which are not consistent with other API.
1582 //-----------------------------------------------------------------------------
1583 
ColorConvertU32ToFloat4(ImU32 in)1584 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1585 {
1586     float s = 1.0f/255.0f;
1587     return ImVec4(
1588         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1589         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1590         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1591         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1592 }
1593 
ColorConvertFloat4ToU32(const ImVec4 & in)1594 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1595 {
1596     ImU32 out;
1597     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1598     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1599     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1600     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1601     return out;
1602 }
1603 
1604 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1605 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
ColorConvertRGBtoHSV(float r,float g,float b,float & out_h,float & out_s,float & out_v)1606 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1607 {
1608     float K = 0.f;
1609     if (g < b)
1610     {
1611         ImSwap(g, b);
1612         K = -1.f;
1613     }
1614     if (r < g)
1615     {
1616         ImSwap(r, g);
1617         K = -2.f / 6.f - K;
1618     }
1619 
1620     const float chroma = r - (g < b ? g : b);
1621     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1622     out_s = chroma / (r + 1e-20f);
1623     out_v = r;
1624 }
1625 
1626 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1627 // also http://en.wikipedia.org/wiki/HSL_and_HSV
ColorConvertHSVtoRGB(float h,float s,float v,float & out_r,float & out_g,float & out_b)1628 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1629 {
1630     if (s == 0.0f)
1631     {
1632         // gray
1633         out_r = out_g = out_b = v;
1634         return;
1635     }
1636 
1637     h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1638     int   i = (int)h;
1639     float f = h - (float)i;
1640     float p = v * (1.0f - s);
1641     float q = v * (1.0f - s * f);
1642     float t = v * (1.0f - s * (1.0f - f));
1643 
1644     switch (i)
1645     {
1646     case 0: out_r = v; out_g = t; out_b = p; break;
1647     case 1: out_r = q; out_g = v; out_b = p; break;
1648     case 2: out_r = p; out_g = v; out_b = t; break;
1649     case 3: out_r = p; out_g = q; out_b = v; break;
1650     case 4: out_r = t; out_g = p; out_b = v; break;
1651     case 5: default: out_r = v; out_g = p; out_b = q; break;
1652     }
1653 }
1654 
GetColorU32(ImGuiCol idx,float alpha_mul)1655 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1656 {
1657     ImGuiStyle& style = GImGui->Style;
1658     ImVec4 c = style.Colors[idx];
1659     c.w *= style.Alpha * alpha_mul;
1660     return ColorConvertFloat4ToU32(c);
1661 }
1662 
GetColorU32(const ImVec4 & col)1663 ImU32 ImGui::GetColorU32(const ImVec4& col)
1664 {
1665     ImGuiStyle& style = GImGui->Style;
1666     ImVec4 c = col;
1667     c.w *= style.Alpha;
1668     return ColorConvertFloat4ToU32(c);
1669 }
1670 
GetStyleColorVec4(ImGuiCol idx)1671 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1672 {
1673     ImGuiStyle& style = GImGui->Style;
1674     return style.Colors[idx];
1675 }
1676 
GetColorU32(ImU32 col)1677 ImU32 ImGui::GetColorU32(ImU32 col)
1678 {
1679     float style_alpha = GImGui->Style.Alpha;
1680     if (style_alpha >= 1.0f)
1681         return col;
1682     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1683     a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1684     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1685 }
1686 
1687 //-----------------------------------------------------------------------------
1688 // [SECTION] ImGuiStorage
1689 // Helper: Key->value storage
1690 //-----------------------------------------------------------------------------
1691 
1692 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1693 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1694 {
1695     ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1696     ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1697     size_t count = (size_t)(last - first);
1698     while (count > 0)
1699     {
1700         size_t count2 = count >> 1;
1701         ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1702         if (mid->key < key)
1703         {
1704             first = ++mid;
1705             count -= count2 + 1;
1706         }
1707         else
1708         {
1709             count = count2;
1710         }
1711     }
1712     return first;
1713 }
1714 
1715 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1716 void ImGuiStorage::BuildSortByKey()
1717 {
1718     struct StaticFunc
1719     {
1720         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1721         {
1722             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1723             if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1724             if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1725             return 0;
1726         }
1727     };
1728     if (Data.Size > 1)
1729         ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1730 }
1731 
GetInt(ImGuiID key,int default_val) const1732 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1733 {
1734     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1735     if (it == Data.end() || it->key != key)
1736         return default_val;
1737     return it->val_i;
1738 }
1739 
GetBool(ImGuiID key,bool default_val) const1740 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1741 {
1742     return GetInt(key, default_val ? 1 : 0) != 0;
1743 }
1744 
GetFloat(ImGuiID key,float default_val) const1745 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1746 {
1747     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1748     if (it == Data.end() || it->key != key)
1749         return default_val;
1750     return it->val_f;
1751 }
1752 
GetVoidPtr(ImGuiID key) const1753 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1754 {
1755     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1756     if (it == Data.end() || it->key != key)
1757         return NULL;
1758     return it->val_p;
1759 }
1760 
1761 // 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.
GetIntRef(ImGuiID key,int default_val)1762 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1763 {
1764     ImVector<Pair>::iterator it = LowerBound(Data, key);
1765     if (it == Data.end() || it->key != key)
1766         it = Data.insert(it, Pair(key, default_val));
1767     return &it->val_i;
1768 }
1769 
GetBoolRef(ImGuiID key,bool default_val)1770 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1771 {
1772     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1773 }
1774 
GetFloatRef(ImGuiID key,float default_val)1775 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1776 {
1777     ImVector<Pair>::iterator it = LowerBound(Data, key);
1778     if (it == Data.end() || it->key != key)
1779         it = Data.insert(it, Pair(key, default_val));
1780     return &it->val_f;
1781 }
1782 
GetVoidPtrRef(ImGuiID key,void * default_val)1783 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1784 {
1785     ImVector<Pair>::iterator it = LowerBound(Data, key);
1786     if (it == Data.end() || it->key != key)
1787         it = Data.insert(it, Pair(key, default_val));
1788     return &it->val_p;
1789 }
1790 
1791 // 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)
SetInt(ImGuiID key,int val)1792 void ImGuiStorage::SetInt(ImGuiID key, int val)
1793 {
1794     ImVector<Pair>::iterator it = LowerBound(Data, key);
1795     if (it == Data.end() || it->key != key)
1796     {
1797         Data.insert(it, Pair(key, val));
1798         return;
1799     }
1800     it->val_i = val;
1801 }
1802 
SetBool(ImGuiID key,bool val)1803 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1804 {
1805     SetInt(key, val ? 1 : 0);
1806 }
1807 
SetFloat(ImGuiID key,float val)1808 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1809 {
1810     ImVector<Pair>::iterator it = LowerBound(Data, key);
1811     if (it == Data.end() || it->key != key)
1812     {
1813         Data.insert(it, Pair(key, val));
1814         return;
1815     }
1816     it->val_f = val;
1817 }
1818 
SetVoidPtr(ImGuiID key,void * val)1819 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1820 {
1821     ImVector<Pair>::iterator it = LowerBound(Data, key);
1822     if (it == Data.end() || it->key != key)
1823     {
1824         Data.insert(it, Pair(key, val));
1825         return;
1826     }
1827     it->val_p = val;
1828 }
1829 
SetAllInt(int v)1830 void ImGuiStorage::SetAllInt(int v)
1831 {
1832     for (int i = 0; i < Data.Size; i++)
1833         Data[i].val_i = v;
1834 }
1835 
1836 //-----------------------------------------------------------------------------
1837 // [SECTION] ImGuiTextFilter
1838 //-----------------------------------------------------------------------------
1839 
1840 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1841 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1842 {
1843     if (default_filter)
1844     {
1845         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1846         Build();
1847     }
1848     else
1849     {
1850         InputBuf[0] = 0;
1851         CountGrep = 0;
1852     }
1853 }
1854 
Draw(const char * label,float width)1855 bool ImGuiTextFilter::Draw(const char* label, float width)
1856 {
1857     if (width != 0.0f)
1858         ImGui::PushItemWidth(width);
1859     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1860     if (width != 0.0f)
1861         ImGui::PopItemWidth();
1862     if (value_changed)
1863         Build();
1864     return value_changed;
1865 }
1866 
split(char separator,ImVector<TextRange> * out) const1867 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
1868 {
1869     out->resize(0);
1870     const char* wb = b;
1871     const char* we = wb;
1872     while (we < e)
1873     {
1874         if (*we == separator)
1875         {
1876             out->push_back(TextRange(wb, we));
1877             wb = we + 1;
1878         }
1879         we++;
1880     }
1881     if (wb != we)
1882         out->push_back(TextRange(wb, we));
1883 }
1884 
Build()1885 void ImGuiTextFilter::Build()
1886 {
1887     Filters.resize(0);
1888     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1889     input_range.split(',', &Filters);
1890 
1891     CountGrep = 0;
1892     for (int i = 0; i != Filters.Size; i++)
1893     {
1894         TextRange& f = Filters[i];
1895         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1896             f.b++;
1897         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1898             f.e--;
1899         if (f.empty())
1900             continue;
1901         if (Filters[i].b[0] != '-')
1902             CountGrep += 1;
1903     }
1904 }
1905 
PassFilter(const char * text,const char * text_end) const1906 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1907 {
1908     if (Filters.empty())
1909         return true;
1910 
1911     if (text == NULL)
1912         text = "";
1913 
1914     for (int i = 0; i != Filters.Size; i++)
1915     {
1916         const TextRange& f = Filters[i];
1917         if (f.empty())
1918             continue;
1919         if (f.b[0] == '-')
1920         {
1921             // Subtract
1922             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1923                 return false;
1924         }
1925         else
1926         {
1927             // Grep
1928             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1929                 return true;
1930         }
1931     }
1932 
1933     // Implicit * grep
1934     if (CountGrep == 0)
1935         return true;
1936 
1937     return false;
1938 }
1939 
1940 //-----------------------------------------------------------------------------
1941 // [SECTION] ImGuiTextBuffer
1942 //-----------------------------------------------------------------------------
1943 
1944 // On some platform vsnprintf() takes va_list by reference and modifies it.
1945 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1946 #ifndef va_copy
1947 #if defined(__GNUC__) || defined(__clang__)
1948 #define va_copy(dest, src) __builtin_va_copy(dest, src)
1949 #else
1950 #define va_copy(dest, src) (dest = src)
1951 #endif
1952 #endif
1953 
1954 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)1955 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
1956 {
1957     va_list args_copy;
1958     va_copy(args_copy, args);
1959 
1960     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1961     if (len <= 0)
1962     {
1963         va_end(args_copy);
1964         return;
1965     }
1966 
1967     const int write_off = Buf.Size;
1968     const int needed_sz = write_off + len;
1969     if (write_off + len >= Buf.Capacity)
1970     {
1971         int double_capacity = Buf.Capacity * 2;
1972         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1973     }
1974 
1975     Buf.resize(needed_sz);
1976     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
1977     va_end(args_copy);
1978 }
1979 
appendf(const char * fmt,...)1980 void ImGuiTextBuffer::appendf(const char* fmt, ...)
1981 {
1982     va_list args;
1983     va_start(args, fmt);
1984     appendfv(fmt, args);
1985     va_end(args);
1986 }
1987 
1988 //-----------------------------------------------------------------------------
1989 // [SECTION] ImGuiListClipper
1990 // This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
1991 //-----------------------------------------------------------------------------
1992 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)1993 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1994 {
1995     // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
1996     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
1997     // The clipper should probably have a 4th step to display the last item in a regular manner.
1998     ImGui::SetCursorPosY(pos_y);
1999     ImGuiWindow* window = ImGui::GetCurrentWindow();
2000     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;      // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
2001     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.
2002     if (window->DC.ColumnsSet)
2003         window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;           // Setting this so that cell Y position are set properly
2004 }
2005 
2006 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2007 // Use case B: Begin() called from constructor with items_height>0
2008 // 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.
Begin(int count,float items_height)2009 void ImGuiListClipper::Begin(int count, float items_height)
2010 {
2011     StartPosY = ImGui::GetCursorPosY();
2012     ItemsHeight = items_height;
2013     ItemsCount = count;
2014     StepNo = 0;
2015     DisplayEnd = DisplayStart = -1;
2016     if (ItemsHeight > 0.0f)
2017     {
2018         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2019         if (DisplayStart > 0)
2020             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2021         StepNo = 2;
2022     }
2023 }
2024 
End()2025 void ImGuiListClipper::End()
2026 {
2027     if (ItemsCount < 0)
2028         return;
2029     // 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.
2030     if (ItemsCount < INT_MAX)
2031         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2032     ItemsCount = -1;
2033     StepNo = 3;
2034 }
2035 
Step()2036 bool ImGuiListClipper::Step()
2037 {
2038     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2039     {
2040         ItemsCount = -1;
2041         return false;
2042     }
2043     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.
2044     {
2045         DisplayStart = 0;
2046         DisplayEnd = 1;
2047         StartPosY = ImGui::GetCursorPosY();
2048         StepNo = 1;
2049         return true;
2050     }
2051     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.
2052     {
2053         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2054         float items_height = ImGui::GetCursorPosY() - StartPosY;
2055         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2056         Begin(ItemsCount-1, items_height);
2057         DisplayStart++;
2058         DisplayEnd++;
2059         StepNo = 3;
2060         return true;
2061     }
2062     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.
2063     {
2064         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2065         StepNo = 3;
2066         return true;
2067     }
2068     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.
2069         End();
2070     return false;
2071 }
2072 
2073 //-----------------------------------------------------------------------------
2074 // [SECTION] RENDER HELPERS
2075 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2076 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2077 //-----------------------------------------------------------------------------
2078 
FindRenderedTextEnd(const char * text,const char * text_end)2079 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2080 {
2081     const char* text_display_end = text;
2082     if (!text_end)
2083         text_end = (const char*)-1;
2084 
2085     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2086         text_display_end++;
2087     return text_display_end;
2088 }
2089 
2090 // Internal ImGui functions to render text
2091 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2092 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2093 {
2094     ImGuiContext& g = *GImGui;
2095     ImGuiWindow* window = g.CurrentWindow;
2096 
2097     // Hide anything after a '##' string
2098     const char* text_display_end;
2099     if (hide_text_after_hash)
2100     {
2101         text_display_end = FindRenderedTextEnd(text, text_end);
2102     }
2103     else
2104     {
2105         if (!text_end)
2106             text_end = text + strlen(text); // FIXME-OPT
2107         text_display_end = text_end;
2108     }
2109 
2110     if (text != text_display_end)
2111     {
2112         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2113         if (g.LogEnabled)
2114             LogRenderedText(&pos, text, text_display_end);
2115     }
2116 }
2117 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2118 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2119 {
2120     ImGuiContext& g = *GImGui;
2121     ImGuiWindow* window = g.CurrentWindow;
2122 
2123     if (!text_end)
2124         text_end = text + strlen(text); // FIXME-OPT
2125 
2126     if (text != text_end)
2127     {
2128         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2129         if (g.LogEnabled)
2130             LogRenderedText(&pos, text, text_end);
2131     }
2132 }
2133 
2134 // Default clip_rect uses (pos_min,pos_max)
2135 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
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)2136 void 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)
2137 {
2138     // Hide anything after a '##' string
2139     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2140     const int text_len = (int)(text_display_end - text);
2141     if (text_len == 0)
2142         return;
2143 
2144     ImGuiContext& g = *GImGui;
2145     ImGuiWindow* window = g.CurrentWindow;
2146 
2147     // Perform CPU side clipping for single clipped element to avoid using scissor state
2148     ImVec2 pos = pos_min;
2149     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2150 
2151     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2152     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2153     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2154     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2155         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2156 
2157     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2158     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2159     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2160 
2161     // Render
2162     if (need_clipping)
2163     {
2164         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2165         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2166     }
2167     else
2168     {
2169         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2170     }
2171     if (g.LogEnabled)
2172         LogRenderedText(&pos, text, text_display_end);
2173 }
2174 
2175 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2176 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2177 {
2178     ImGuiContext& g = *GImGui;
2179     ImGuiWindow* window = g.CurrentWindow;
2180     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2181     const float border_size = g.Style.FrameBorderSize;
2182     if (border && border_size > 0.0f)
2183     {
2184         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2185         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2186     }
2187 }
2188 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2189 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2190 {
2191     ImGuiContext& g = *GImGui;
2192     ImGuiWindow* window = g.CurrentWindow;
2193     const float border_size = g.Style.FrameBorderSize;
2194     if (border_size > 0.0f)
2195     {
2196         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2197         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2198     }
2199 }
2200 
2201 // 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
RenderArrow(ImVec2 p_min,ImGuiDir dir,float scale)2202 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2203 {
2204     ImGuiContext& g = *GImGui;
2205 
2206     const float h = g.FontSize * 1.00f;
2207     float r = h * 0.40f * scale;
2208     ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2209 
2210     ImVec2 a, b, c;
2211     switch (dir)
2212     {
2213     case ImGuiDir_Up:
2214     case ImGuiDir_Down:
2215         if (dir == ImGuiDir_Up) r = -r;
2216         a = ImVec2(+0.000f,+0.750f) * r;
2217         b = ImVec2(-0.866f,-0.750f) * r;
2218         c = ImVec2(+0.866f,-0.750f) * r;
2219         break;
2220     case ImGuiDir_Left:
2221     case ImGuiDir_Right:
2222         if (dir == ImGuiDir_Left) r = -r;
2223         a = ImVec2(+0.750f,+0.000f) * r;
2224         b = ImVec2(-0.750f,+0.866f) * r;
2225         c = ImVec2(-0.750f,-0.866f) * r;
2226         break;
2227     case ImGuiDir_None:
2228     case ImGuiDir_COUNT:
2229         IM_ASSERT(0);
2230         break;
2231     }
2232 
2233     g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
2234 }
2235 
RenderBullet(ImVec2 pos)2236 void ImGui::RenderBullet(ImVec2 pos)
2237 {
2238     ImGuiContext& g = *GImGui;
2239     ImGuiWindow* window = g.CurrentWindow;
2240     window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
2241 }
2242 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2243 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2244 {
2245     ImGuiContext& g = *GImGui;
2246     ImGuiWindow* window = g.CurrentWindow;
2247 
2248     float thickness = ImMax(sz / 5.0f, 1.0f);
2249     sz -= thickness*0.5f;
2250     pos += ImVec2(thickness*0.25f, thickness*0.25f);
2251 
2252     float third = sz / 3.0f;
2253     float bx = pos.x + third;
2254     float by = pos.y + sz - third*0.5f;
2255     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2256     window->DrawList->PathLineTo(ImVec2(bx, by));
2257     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2258     window->DrawList->PathStroke(col, false, thickness);
2259 }
2260 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2261 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2262 {
2263     ImGuiContext& g = *GImGui;
2264     if (id != g.NavId)
2265         return;
2266     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2267         return;
2268     ImGuiWindow* window = ImGui::GetCurrentWindow();
2269     if (window->DC.NavHideHighlightOneFrame)
2270         return;
2271 
2272     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2273     ImRect display_rect = bb;
2274     display_rect.ClipWith(window->ClipRect);
2275     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2276     {
2277         const float THICKNESS = 2.0f;
2278         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2279         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2280         bool fully_visible = window->ClipRect.Contains(display_rect);
2281         if (!fully_visible)
2282             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2283         window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
2284         if (!fully_visible)
2285             window->DrawList->PopClipRect();
2286     }
2287     if (flags & ImGuiNavHighlightFlags_TypeThin)
2288     {
2289         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2290     }
2291 }
2292 
2293 //-----------------------------------------------------------------------------
2294 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2295 //-----------------------------------------------------------------------------
2296 
2297 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2298 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2299     : DrawListInst(&context->DrawListSharedData)
2300 {
2301     Name = ImStrdup(name);
2302     ID = ImHash(name, 0);
2303     IDStack.push_back(ID);
2304     Flags = 0;
2305     Pos = ImVec2(0.0f, 0.0f);
2306     Size = SizeFull = ImVec2(0.0f, 0.0f);
2307     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2308     WindowPadding = ImVec2(0.0f, 0.0f);
2309     WindowRounding = 0.0f;
2310     WindowBorderSize = 0.0f;
2311     MoveId = GetID("#MOVE");
2312     ChildId = 0;
2313     Scroll = ImVec2(0.0f, 0.0f);
2314     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2315     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2316     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2317     ScrollbarX = ScrollbarY = false;
2318     Active = WasActive = false;
2319     WriteAccessed = false;
2320     Collapsed = false;
2321     WantCollapseToggle = false;
2322     SkipItems = false;
2323     Appearing = false;
2324     Hidden = false;
2325     HasCloseButton = false;
2326     BeginCount = 0;
2327     BeginOrderWithinParent = -1;
2328     BeginOrderWithinContext = -1;
2329     PopupId = 0;
2330     AutoFitFramesX = AutoFitFramesY = -1;
2331     AutoFitOnlyGrows = false;
2332     AutoFitChildAxises = 0x00;
2333     AutoPosLastDirection = ImGuiDir_None;
2334     HiddenFramesRegular = HiddenFramesForResize = 0;
2335     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2336     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2337 
2338     LastFrameActive = -1;
2339     ItemWidthDefault = 0.0f;
2340     FontWindowScale = 1.0f;
2341     SettingsIdx = -1;
2342 
2343     DrawList = &DrawListInst;
2344     DrawList->_OwnerName = Name;
2345     ParentWindow = NULL;
2346     RootWindow = NULL;
2347     RootWindowForTitleBarHighlight = NULL;
2348     RootWindowForNav = NULL;
2349 
2350     NavLastIds[0] = NavLastIds[1] = 0;
2351     NavRectRel[0] = NavRectRel[1] = ImRect();
2352     NavLastChildNavWindow = NULL;
2353 
2354     FocusIdxAllCounter = FocusIdxTabCounter = -1;
2355     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2356     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2357 }
2358 
~ImGuiWindow()2359 ImGuiWindow::~ImGuiWindow()
2360 {
2361     IM_ASSERT(DrawList == &DrawListInst);
2362     IM_DELETE(Name);
2363     for (int i = 0; i != ColumnsStorage.Size; i++)
2364         ColumnsStorage[i].~ImGuiColumnsSet();
2365 }
2366 
GetID(const char * str,const char * str_end)2367 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2368 {
2369     ImGuiID seed = IDStack.back();
2370     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2371     ImGui::KeepAliveID(id);
2372     return id;
2373 }
2374 
GetID(const void * ptr)2375 ImGuiID ImGuiWindow::GetID(const void* ptr)
2376 {
2377     ImGuiID seed = IDStack.back();
2378     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
2379     ImGui::KeepAliveID(id);
2380     return id;
2381 }
2382 
GetIDNoKeepAlive(const char * str,const char * str_end)2383 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2384 {
2385     ImGuiID seed = IDStack.back();
2386     return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2387 }
2388 
GetIDNoKeepAlive(const void * ptr)2389 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2390 {
2391     ImGuiID seed = IDStack.back();
2392     return ImHash(&ptr, sizeof(void*), seed);
2393 }
2394 
2395 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2396 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2397 {
2398     ImGuiID seed = IDStack.back();
2399     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) };
2400     ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
2401     ImGui::KeepAliveID(id);
2402     return id;
2403 }
2404 
SetCurrentWindow(ImGuiWindow * window)2405 static void SetCurrentWindow(ImGuiWindow* window)
2406 {
2407     ImGuiContext& g = *GImGui;
2408     g.CurrentWindow = window;
2409     if (window)
2410         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2411 }
2412 
SetNavID(ImGuiID id,int nav_layer)2413 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2414 {
2415     ImGuiContext& g = *GImGui;
2416     IM_ASSERT(g.NavWindow);
2417     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2418     g.NavId = id;
2419     g.NavWindow->NavLastIds[nav_layer] = id;
2420 }
2421 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2422 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2423 {
2424     ImGuiContext& g = *GImGui;
2425     SetNavID(id, nav_layer);
2426     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2427     g.NavMousePosDirty = true;
2428     g.NavDisableHighlight = false;
2429     g.NavDisableMouseHover = true;
2430 }
2431 
SetActiveID(ImGuiID id,ImGuiWindow * window)2432 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2433 {
2434     ImGuiContext& g = *GImGui;
2435     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2436     if (g.ActiveIdIsJustActivated)
2437     {
2438         g.ActiveIdTimer = 0.0f;
2439         g.ActiveIdHasBeenEdited = false;
2440         if (id != 0)
2441         {
2442             g.LastActiveId = id;
2443             g.LastActiveIdTimer = 0.0f;
2444         }
2445     }
2446     g.ActiveId = id;
2447     g.ActiveIdAllowNavDirFlags = 0;
2448     g.ActiveIdAllowOverlap = false;
2449     g.ActiveIdWindow = window;
2450     if (id)
2451     {
2452         g.ActiveIdIsAlive = id;
2453         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2454     }
2455 }
2456 
SetFocusID(ImGuiID id,ImGuiWindow * window)2457 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2458 {
2459     ImGuiContext& g = *GImGui;
2460     IM_ASSERT(id != 0);
2461 
2462     // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2463     const int nav_layer = window->DC.NavLayerCurrent;
2464     if (g.NavWindow != window)
2465         g.NavInitRequest = false;
2466     g.NavId = id;
2467     g.NavWindow = window;
2468     g.NavLayer = nav_layer;
2469     window->NavLastIds[nav_layer] = id;
2470     if (window->DC.LastItemId == id)
2471         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2472 
2473     if (g.ActiveIdSource == ImGuiInputSource_Nav)
2474         g.NavDisableMouseHover = true;
2475     else
2476         g.NavDisableHighlight = true;
2477 }
2478 
ClearActiveID()2479 void ImGui::ClearActiveID()
2480 {
2481     SetActiveID(0, NULL);
2482 }
2483 
SetHoveredID(ImGuiID id)2484 void ImGui::SetHoveredID(ImGuiID id)
2485 {
2486     ImGuiContext& g = *GImGui;
2487     g.HoveredId = id;
2488     g.HoveredIdAllowOverlap = false;
2489     if (id != 0 && g.HoveredIdPreviousFrame != id)
2490         g.HoveredIdTimer = 0.0f;
2491 }
2492 
GetHoveredID()2493 ImGuiID ImGui::GetHoveredID()
2494 {
2495     ImGuiContext& g = *GImGui;
2496     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2497 }
2498 
KeepAliveID(ImGuiID id)2499 void ImGui::KeepAliveID(ImGuiID id)
2500 {
2501     ImGuiContext& g = *GImGui;
2502     if (g.ActiveId == id)
2503         g.ActiveIdIsAlive = id;
2504     if (g.ActiveIdPreviousFrame == id)
2505         g.ActiveIdPreviousFrameIsAlive = true;
2506 }
2507 
MarkItemEdited(ImGuiID id)2508 void ImGui::MarkItemEdited(ImGuiID id)
2509 {
2510     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2511     // 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.
2512     (void)id; // Avoid unused variable warnings when asserts are compiled out.
2513     ImGuiContext& g = *GImGui;
2514     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2515     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2516     g.ActiveIdHasBeenEdited = true;
2517     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2518 }
2519 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2520 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2521 {
2522     // An active popup disable hovering on other windows (apart from its own children)
2523     // FIXME-OPT: This could be cached/stored within the window.
2524     ImGuiContext& g = *GImGui;
2525     if (g.NavWindow)
2526         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2527             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2528             {
2529                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2530                 // NB: The order of those two tests is important because Modal windows are also Popups.
2531                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2532                     return false;
2533                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2534                     return false;
2535             }
2536 
2537     return true;
2538 }
2539 
2540 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2541 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2542 {
2543     ImGuiContext& g = *GImGui;
2544     ImGuiWindow* window = g.CurrentWindow;
2545     if (window->SkipItems)
2546         return;
2547 
2548     // Always align ourselves on pixel boundaries
2549     const float line_height = ImMax(window->DC.CurrentLineSize.y, size.y);
2550     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2551     //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]
2552     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2553     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));
2554     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2555     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2556     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2557 
2558     window->DC.PrevLineSize.y = line_height;
2559     window->DC.PrevLineTextBaseOffset = text_base_offset;
2560     window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2561 
2562     // Horizontal layout mode
2563     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2564         SameLine();
2565 }
2566 
ItemSize(const ImRect & bb,float text_offset_y)2567 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2568 {
2569     ItemSize(bb.GetSize(), text_offset_y);
2570 }
2571 
2572 // Declare item bounding box for clipping and interaction.
2573 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2574 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)2575 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2576 {
2577     ImGuiContext& g = *GImGui;
2578     ImGuiWindow* window = g.CurrentWindow;
2579 
2580     if (id != 0)
2581     {
2582         // Navigation processing runs prior to clipping early-out
2583         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2584         //  (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.
2585         //      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.
2586         //      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)
2587         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2588         if (g.NavId == id || g.NavAnyRequest)
2589             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2590                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2591                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2592     }
2593 
2594     window->DC.LastItemId = id;
2595     window->DC.LastItemRect = bb;
2596     window->DC.LastItemStatusFlags = 0;
2597 
2598     // Clipping test
2599     const bool is_clipped = IsClippedEx(bb, id, false);
2600     if (is_clipped)
2601         return false;
2602     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2603 
2604     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2605     if (IsMouseHoveringRect(bb.Min, bb.Max))
2606         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2607     return true;
2608 }
2609 
2610 // This is roughly matching the behavior of internal-facing ItemHoverable()
2611 // - 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()
2612 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2613 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2614 {
2615     ImGuiContext& g = *GImGui;
2616     ImGuiWindow* window = g.CurrentWindow;
2617     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2618         return IsItemFocused();
2619 
2620     // Test for bounding box overlap, as updated as ItemAdd()
2621     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2622         return false;
2623     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2624 
2625     // Test if we are hovering the right window (our window could be behind another window)
2626     // [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.
2627     // 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.
2628     //if (g.HoveredWindow != window)
2629     //    return false;
2630     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2631         return false;
2632 
2633     // Test if another item is active (e.g. being dragged)
2634     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2635         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2636             return false;
2637 
2638     // Test if interactions on this window are blocked by an active popup or modal
2639     if (!IsWindowContentHoverable(window, flags))
2640         return false;
2641 
2642     // Test if the item is disabled
2643     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2644         return false;
2645 
2646     // 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.
2647     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2648         return false;
2649     return true;
2650 }
2651 
2652 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2653 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2654 {
2655     ImGuiContext& g = *GImGui;
2656     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2657         return false;
2658 
2659     ImGuiWindow* window = g.CurrentWindow;
2660     if (g.HoveredWindow != window)
2661         return false;
2662     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2663         return false;
2664     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2665         return false;
2666     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2667         return false;
2668     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2669         return false;
2670 
2671     SetHoveredID(id);
2672     return true;
2673 }
2674 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2675 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2676 {
2677     ImGuiContext& g = *GImGui;
2678     ImGuiWindow* window = g.CurrentWindow;
2679     if (!bb.Overlaps(window->ClipRect))
2680         if (id == 0 || id != g.ActiveId)
2681             if (clip_even_when_logged || !g.LogEnabled)
2682                 return true;
2683     return false;
2684 }
2685 
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2686 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2687 {
2688     ImGuiContext& g = *GImGui;
2689 
2690     const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus;
2691     window->FocusIdxAllCounter++;
2692     if (allow_keyboard_focus)
2693         window->FocusIdxTabCounter++;
2694 
2695     // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2696     // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2697     if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2698         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2699 
2700     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2701         return true;
2702     if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2703     {
2704         g.NavJustTabbedId = id;
2705         return true;
2706     }
2707 
2708     return false;
2709 }
2710 
FocusableItemUnregister(ImGuiWindow * window)2711 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2712 {
2713     window->FocusIdxAllCounter--;
2714     window->FocusIdxTabCounter--;
2715 }
2716 
CalcItemSize(ImVec2 size,float default_x,float default_y)2717 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2718 {
2719     ImGuiContext& g = *GImGui;
2720     ImVec2 content_max;
2721     if (size.x < 0.0f || size.y < 0.0f)
2722         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2723     if (size.x <= 0.0f)
2724         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2725     if (size.y <= 0.0f)
2726         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2727     return size;
2728 }
2729 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2730 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2731 {
2732     if (wrap_pos_x < 0.0f)
2733         return 0.0f;
2734 
2735     ImGuiWindow* window = GetCurrentWindowRead();
2736     if (wrap_pos_x == 0.0f)
2737         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2738     else if (wrap_pos_x > 0.0f)
2739         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2740 
2741     return ImMax(wrap_pos_x - pos.x, 1.0f);
2742 }
2743 
MemAlloc(size_t size)2744 void* ImGui::MemAlloc(size_t size)
2745 {
2746     if (ImGuiContext* ctx = GImGui)
2747         ctx->IO.MetricsActiveAllocations++;
2748     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2749 }
2750 
MemFree(void * ptr)2751 void ImGui::MemFree(void* ptr)
2752 {
2753     if (ptr)
2754         if (ImGuiContext* ctx = GImGui)
2755             ctx->IO.MetricsActiveAllocations--;
2756     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2757 }
2758 
GetClipboardText()2759 const char* ImGui::GetClipboardText()
2760 {
2761     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2762 }
2763 
SetClipboardText(const char * text)2764 void ImGui::SetClipboardText(const char* text)
2765 {
2766     if (GImGui->IO.SetClipboardTextFn)
2767         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2768 }
2769 
GetVersion()2770 const char* ImGui::GetVersion()
2771 {
2772     return IMGUI_VERSION;
2773 }
2774 
2775 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2776 // 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
GetCurrentContext()2777 ImGuiContext* ImGui::GetCurrentContext()
2778 {
2779     return GImGui;
2780 }
2781 
SetCurrentContext(ImGuiContext * ctx)2782 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2783 {
2784 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2785     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2786 #else
2787     GImGui = ctx;
2788 #endif
2789 }
2790 
2791 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
2792 // 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.
DebugCheckVersionAndDataLayout(const char * version,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_vert)2793 bool 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)
2794 {
2795     bool error = false;
2796     if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!");  }
2797     if (sz_io    != sizeof(ImGuiIO))       { error = true; IM_ASSERT(sz_io    == sizeof(ImGuiIO)      && "Mismatched struct layout!"); }
2798     if (sz_style != sizeof(ImGuiStyle))    { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle)   && "Mismatched struct layout!"); }
2799     if (sz_vec2  != sizeof(ImVec2))        { error = true; IM_ASSERT(sz_vec2  == sizeof(ImVec2)       && "Mismatched struct layout!"); }
2800     if (sz_vec4  != sizeof(ImVec4))        { error = true; IM_ASSERT(sz_vec4  == sizeof(ImVec4)       && "Mismatched struct layout!"); }
2801     if (sz_vert  != sizeof(ImDrawVert))    { error = true; IM_ASSERT(sz_vert  == sizeof(ImDrawVert)   && "Mismatched struct layout!"); }
2802     return !error;
2803 }
2804 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)2805 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
2806 {
2807     GImAllocatorAllocFunc = alloc_func;
2808     GImAllocatorFreeFunc = free_func;
2809     GImAllocatorUserData = user_data;
2810 }
2811 
CreateContext(ImFontAtlas * shared_font_atlas)2812 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2813 {
2814     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2815     if (GImGui == NULL)
2816         SetCurrentContext(ctx);
2817     Initialize(ctx);
2818     return ctx;
2819 }
2820 
DestroyContext(ImGuiContext * ctx)2821 void ImGui::DestroyContext(ImGuiContext* ctx)
2822 {
2823     if (ctx == NULL)
2824         ctx = GImGui;
2825     Shutdown(ctx);
2826     if (GImGui == ctx)
2827         SetCurrentContext(NULL);
2828     IM_DELETE(ctx);
2829 }
2830 
GetIO()2831 ImGuiIO& ImGui::GetIO()
2832 {
2833     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2834     return GImGui->IO;
2835 }
2836 
GetStyle()2837 ImGuiStyle& ImGui::GetStyle()
2838 {
2839     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2840     return GImGui->Style;
2841 }
2842 
2843 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()2844 ImDrawData* ImGui::GetDrawData()
2845 {
2846     ImGuiContext& g = *GImGui;
2847     return g.DrawData.Valid ? &g.DrawData : NULL;
2848 }
2849 
GetTime()2850 double ImGui::GetTime()
2851 {
2852     return GImGui->Time;
2853 }
2854 
GetFrameCount()2855 int ImGui::GetFrameCount()
2856 {
2857     return GImGui->FrameCount;
2858 }
2859 
GetOverlayDrawList()2860 ImDrawList* ImGui::GetOverlayDrawList()
2861 {
2862     return &GImGui->OverlayDrawList;
2863 }
2864 
GetDrawListSharedData()2865 ImDrawListSharedData* ImGui::GetDrawListSharedData()
2866 {
2867     return &GImGui->DrawListSharedData;
2868 }
2869 
StartMouseMovingWindow(ImGuiWindow * window)2870 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
2871 {
2872     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
2873     ImGuiContext& g = *GImGui;
2874     FocusWindow(window);
2875     SetActiveID(window->MoveId, window);
2876     g.NavDisableHighlight = true;
2877     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
2878     if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
2879         g.MovingWindow = window;
2880 }
2881 
2882 // Handle mouse moving window
2883 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindow()2884 void ImGui::UpdateMouseMovingWindow()
2885 {
2886     ImGuiContext& g = *GImGui;
2887     if (g.MovingWindow != NULL)
2888     {
2889         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
2890         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
2891         KeepAliveID(g.ActiveId);
2892         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
2893         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
2894         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
2895         {
2896             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
2897             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
2898             {
2899                 MarkIniSettingsDirty(moving_window);
2900                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
2901             }
2902             FocusWindow(g.MovingWindow);
2903         }
2904         else
2905         {
2906             ClearActiveID();
2907             g.MovingWindow = NULL;
2908         }
2909     }
2910     else
2911     {
2912         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
2913         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
2914         {
2915             KeepAliveID(g.ActiveId);
2916             if (!g.IO.MouseDown[0])
2917                 ClearActiveID();
2918         }
2919     }
2920 }
2921 
IsWindowActiveAndVisible(ImGuiWindow * window)2922 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
2923 {
2924     return (window->Active) && (!window->Hidden);
2925 }
2926 
UpdateMouseInputs()2927 static void ImGui::UpdateMouseInputs()
2928 {
2929     ImGuiContext& g = *GImGui;
2930 
2931     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
2932     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
2933         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2934     else
2935         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2936     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
2937         g.NavDisableMouseHover = false;
2938 
2939     g.IO.MousePosPrev = g.IO.MousePos;
2940     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2941     {
2942         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2943         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2944         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2945         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;
2946         g.IO.MouseDoubleClicked[i] = false;
2947         if (g.IO.MouseClicked[i])
2948         {
2949             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
2950             {
2951                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
2952                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
2953                     g.IO.MouseDoubleClicked[i] = true;
2954                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
2955             }
2956             else
2957             {
2958                 g.IO.MouseClickedTime[i] = g.Time;
2959             }
2960             g.IO.MouseClickedPos[i] = g.IO.MousePos;
2961             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
2962             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
2963         }
2964         else if (g.IO.MouseDown[i])
2965         {
2966             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
2967             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
2968             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
2969             g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
2970             g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
2971         }
2972         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
2973             g.NavDisableMouseHover = false;
2974     }
2975 }
2976 
UpdateMouseWheel()2977 void ImGui::UpdateMouseWheel()
2978 {
2979     ImGuiContext& g = *GImGui;
2980     if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
2981         return;
2982     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
2983         return;
2984 
2985     // 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).
2986     ImGuiWindow* window = g.HoveredWindow;
2987     ImGuiWindow* scroll_window = window;
2988     while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow)
2989         scroll_window = scroll_window->ParentWindow;
2990     const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs);
2991 
2992     if (g.IO.MouseWheel != 0.0f)
2993     {
2994         if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
2995         {
2996             // Zoom / Scale window
2997             const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2998             const float scale = new_font_scale / window->FontWindowScale;
2999             window->FontWindowScale = new_font_scale;
3000 
3001             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3002             window->Pos += offset;
3003             window->Size *= scale;
3004             window->SizeFull *= scale;
3005         }
3006         else if (!g.IO.KeyCtrl && scroll_allowed)
3007         {
3008             // Mouse wheel vertical scrolling
3009             float scroll_amount = 5 * scroll_window->CalcFontSize();
3010             scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3011             SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3012         }
3013     }
3014     if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3015     {
3016         // Mouse wheel horizontal scrolling (for hardware that supports it)
3017         float scroll_amount = scroll_window->CalcFontSize();
3018         SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3019     }
3020 }
3021 
3022 // 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)
UpdateHoveredWindowAndCaptureFlags()3023 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3024 {
3025     ImGuiContext& g = *GImGui;
3026 
3027     // Find the window hovered by mouse:
3028     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3029     // - 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.
3030     // - 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.
3031     FindHoveredWindow();
3032 
3033     // Modal windows prevents cursor from hovering behind them.
3034     ImGuiWindow* modal_window = GetFrontMostPopupModal();
3035     if (modal_window)
3036         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3037             g.HoveredRootWindow = g.HoveredWindow = NULL;
3038 
3039     // Disabled mouse?
3040     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3041         g.HoveredWindow = g.HoveredRootWindow = NULL;
3042 
3043     // 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.
3044     int mouse_earliest_button_down = -1;
3045     bool mouse_any_down = false;
3046     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3047     {
3048         if (g.IO.MouseClicked[i])
3049             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3050         mouse_any_down |= g.IO.MouseDown[i];
3051         if (g.IO.MouseDown[i])
3052             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3053                 mouse_earliest_button_down = i;
3054     }
3055     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3056 
3057     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3058     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3059     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3060     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3061         g.HoveredWindow = g.HoveredRootWindow = NULL;
3062 
3063     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3064     if (g.WantCaptureMouseNextFrame != -1)
3065         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3066     else
3067         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3068 
3069     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3070     if (g.WantCaptureKeyboardNextFrame != -1)
3071         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3072     else
3073         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3074     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3075         g.IO.WantCaptureKeyboard = true;
3076 
3077     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3078     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3079 }
3080 
NewFrame()3081 void ImGui::NewFrame()
3082 {
3083     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3084     ImGuiContext& g = *GImGui;
3085 
3086     // Check user data
3087     // (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)
3088     IM_ASSERT(g.Initialized);
3089     IM_ASSERT(g.IO.DeltaTime >= 0.0f                                    && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3090     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value");
3091     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3092     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3093     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting");
3094     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)");
3095     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3096     for (int n = 0; n < ImGuiKey_COUNT; n++)
3097         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)");
3098 
3099     // 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)
3100     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3101         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3102 
3103     // Perform simple check: the beta io.ConfigResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3104     if (g.IO.ConfigResizeWindowsFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3105         g.IO.ConfigResizeWindowsFromEdges = false;
3106 
3107     // Load settings on first frame (if not explicitly loaded manually before)
3108     if (!g.SettingsLoaded)
3109     {
3110         IM_ASSERT(g.SettingsWindows.empty());
3111         if (g.IO.IniFilename)
3112             LoadIniSettingsFromDisk(g.IO.IniFilename);
3113         g.SettingsLoaded = true;
3114     }
3115 
3116     // Save settings (with a delay after the last modification, so we don't spam disk too much)
3117     if (g.SettingsDirtyTimer > 0.0f)
3118     {
3119         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3120         if (g.SettingsDirtyTimer <= 0.0f)
3121         {
3122             if (g.IO.IniFilename != NULL)
3123                 SaveIniSettingsToDisk(g.IO.IniFilename);
3124             else
3125                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3126             g.SettingsDirtyTimer = 0.0f;
3127         }
3128     }
3129 
3130     g.Time += g.IO.DeltaTime;
3131     g.FrameScopeActive = true;
3132     g.FrameCount += 1;
3133     g.TooltipOverrideCount = 0;
3134     g.WindowsActiveCount = 0;
3135 
3136     // Setup current font and draw list
3137     g.IO.Fonts->Locked = true;
3138     SetCurrentFont(GetDefaultFont());
3139     IM_ASSERT(g.Font->IsLoaded());
3140     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3141     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3142 
3143     g.OverlayDrawList.Clear();
3144     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3145     g.OverlayDrawList.PushClipRectFullScreen();
3146     g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3147 
3148     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3149     g.DrawData.Clear();
3150 
3151     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3152     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3153         KeepAliveID(g.DragDropPayload.SourceId);
3154 
3155     // Clear reference to active widget if the widget isn't alive anymore
3156     if (!g.HoveredIdPreviousFrame)
3157         g.HoveredIdTimer = 0.0f;
3158     if (g.HoveredId)
3159         g.HoveredIdTimer += g.IO.DeltaTime;
3160     g.HoveredIdPreviousFrame = g.HoveredId;
3161     g.HoveredId = 0;
3162     g.HoveredIdAllowOverlap = false;
3163     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3164         ClearActiveID();
3165     if (g.ActiveId)
3166         g.ActiveIdTimer += g.IO.DeltaTime;
3167     g.LastActiveIdTimer += g.IO.DeltaTime;
3168     g.ActiveIdPreviousFrame = g.ActiveId;
3169     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3170     g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3171     g.ActiveIdIsAlive = 0;
3172     g.ActiveIdPreviousFrameIsAlive = false;
3173     g.ActiveIdIsJustActivated = false;
3174     if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3175         g.ScalarAsInputTextId = 0;
3176 
3177     // Drag and drop
3178     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3179     g.DragDropAcceptIdCurr = 0;
3180     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3181     g.DragDropWithinSourceOrTarget = false;
3182 
3183     // Update keyboard input state
3184     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3185     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3186         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;
3187 
3188     // Update gamepad/keyboard directional navigation
3189     NavUpdate();
3190 
3191     // Update mouse input state
3192     UpdateMouseInputs();
3193 
3194     // Calculate frame-rate for the user, as a purely luxurious feature
3195     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3196     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3197     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3198     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3199 
3200     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3201     UpdateMouseMovingWindow();
3202     UpdateHoveredWindowAndCaptureFlags();
3203 
3204     // Background darkening/whitening
3205     if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3206         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3207     else
3208         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3209 
3210     g.MouseCursor = ImGuiMouseCursor_Arrow;
3211     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3212     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3213 
3214     // Mouse wheel scrolling, scale
3215     UpdateMouseWheel();
3216 
3217     // Pressing TAB activate widget focus
3218     if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3219     {
3220         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3221             g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3222         else
3223             g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3224     }
3225     g.NavIdTabCounter = INT_MAX;
3226 
3227     // Mark all windows as not visible
3228     for (int i = 0; i != g.Windows.Size; i++)
3229     {
3230         ImGuiWindow* window = g.Windows[i];
3231         window->WasActive = window->Active;
3232         window->Active = false;
3233         window->WriteAccessed = false;
3234     }
3235 
3236     // Closing the focused window restore focus to the first active root window in descending z-order
3237     if (g.NavWindow && !g.NavWindow->WasActive)
3238         FocusFrontMostActiveWindowIgnoringOne(NULL);
3239 
3240     // No window should be open at the beginning of the frame.
3241     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3242     g.CurrentWindowStack.resize(0);
3243     g.CurrentPopupStack.resize(0);
3244     ClosePopupsOverWindow(g.NavWindow);
3245 
3246     // Create implicit window - we will only render it if the user has added something to it.
3247     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3248     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3249     Begin("Debug##Default");
3250 }
3251 
Initialize(ImGuiContext * context)3252 void ImGui::Initialize(ImGuiContext* context)
3253 {
3254     ImGuiContext& g = *context;
3255     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3256 
3257     // Add .ini handle for ImGuiWindow type
3258     ImGuiSettingsHandler ini_handler;
3259     ini_handler.TypeName = "Window";
3260     ini_handler.TypeHash = ImHash("Window", 0, 0);
3261     ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3262     ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3263     ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3264     g.SettingsHandlers.push_front(ini_handler);
3265 
3266     g.Initialized = true;
3267 }
3268 
3269 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3270 void ImGui::Shutdown(ImGuiContext* context)
3271 {
3272     // 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)
3273     ImGuiContext& g = *context;
3274     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3275         IM_DELETE(g.IO.Fonts);
3276     g.IO.Fonts = NULL;
3277 
3278     // Cleanup of other data are conditional on actually having initialized ImGui.
3279     if (!g.Initialized)
3280         return;
3281 
3282     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3283     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3284         SaveIniSettingsToDisk(g.IO.IniFilename);
3285 
3286     // Clear everything else
3287     for (int i = 0; i < g.Windows.Size; i++)
3288         IM_DELETE(g.Windows[i]);
3289     g.Windows.clear();
3290     g.WindowsSortBuffer.clear();
3291     g.CurrentWindow = NULL;
3292     g.CurrentWindowStack.clear();
3293     g.WindowsById.Clear();
3294     g.NavWindow = NULL;
3295     g.HoveredWindow = NULL;
3296     g.HoveredRootWindow = NULL;
3297     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3298     g.MovingWindow = NULL;
3299     g.ColorModifiers.clear();
3300     g.StyleModifiers.clear();
3301     g.FontStack.clear();
3302     g.OpenPopupStack.clear();
3303     g.CurrentPopupStack.clear();
3304     g.DrawDataBuilder.ClearFreeMemory();
3305     g.OverlayDrawList.ClearFreeMemory();
3306     g.PrivateClipboard.clear();
3307     g.InputTextState.TextW.clear();
3308     g.InputTextState.InitialText.clear();
3309     g.InputTextState.TempBuffer.clear();
3310 
3311     for (int i = 0; i < g.SettingsWindows.Size; i++)
3312         IM_DELETE(g.SettingsWindows[i].Name);
3313     g.SettingsWindows.clear();
3314     g.SettingsHandlers.clear();
3315 
3316     if (g.LogFile && g.LogFile != stdout)
3317     {
3318         fclose(g.LogFile);
3319         g.LogFile = NULL;
3320     }
3321     g.LogClipboard.clear();
3322 
3323     g.Initialized = false;
3324 }
3325 
3326 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3327 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3328 {
3329     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3330     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3331     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3332         return d;
3333     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3334         return d;
3335     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3336 }
3337 
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3338 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3339 {
3340     out_sorted_windows->push_back(window);
3341     if (window->Active)
3342     {
3343         int count = window->DC.ChildWindows.Size;
3344         if (count > 1)
3345             ImQsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3346         for (int i = 0; i < count; i++)
3347         {
3348             ImGuiWindow* child = window->DC.ChildWindows[i];
3349             if (child->Active)
3350                 AddWindowToSortedBuffer(out_sorted_windows, child);
3351         }
3352     }
3353 }
3354 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3355 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3356 {
3357     if (draw_list->CmdBuffer.empty())
3358         return;
3359 
3360     // Remove trailing command if unused
3361     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3362     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3363     {
3364         draw_list->CmdBuffer.pop_back();
3365         if (draw_list->CmdBuffer.empty())
3366             return;
3367     }
3368 
3369     // 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.
3370     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3371     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3372     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3373 
3374     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3375     // If this assert triggers because you are drawing lots of stuff manually:
3376     // 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.
3377     // 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.
3378     //    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:
3379     //      glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3380     //    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.
3381     // 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.
3382     if (sizeof(ImDrawIdx) == 2)
3383         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3384 
3385     out_list->push_back(draw_list);
3386 }
3387 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3388 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3389 {
3390     ImGuiContext& g = *GImGui;
3391     g.IO.MetricsRenderWindows++;
3392     AddDrawListToDrawData(out_render_list, window->DrawList);
3393     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3394     {
3395         ImGuiWindow* child = window->DC.ChildWindows[i];
3396         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3397             AddWindowToDrawData(out_render_list, child);
3398     }
3399 }
3400 
AddWindowToDrawDataSelectLayer(ImGuiWindow * window)3401 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3402 {
3403     ImGuiContext& g = *GImGui;
3404     if (window->Flags & ImGuiWindowFlags_Tooltip)
3405         AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3406     else
3407         AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3408 }
3409 
FlattenIntoSingleLayer()3410 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3411 {
3412     int n = Layers[0].Size;
3413     int size = n;
3414     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3415         size += Layers[i].Size;
3416     Layers[0].resize(size);
3417     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3418     {
3419         ImVector<ImDrawList*>& layer = Layers[layer_n];
3420         if (layer.empty())
3421             continue;
3422         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3423         n += layer.Size;
3424         layer.resize(0);
3425     }
3426 }
3427 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3428 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3429 {
3430     ImGuiIO& io = ImGui::GetIO();
3431     draw_data->Valid = true;
3432     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3433     draw_data->CmdListsCount = draw_lists->Size;
3434     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3435     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3436     draw_data->DisplaySize = io.DisplaySize;
3437     for (int n = 0; n < draw_lists->Size; n++)
3438     {
3439         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3440         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3441     }
3442 }
3443 
3444 // 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.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)3445 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3446 {
3447     ImGuiWindow* window = GetCurrentWindow();
3448     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3449     window->ClipRect = window->DrawList->_ClipRectStack.back();
3450 }
3451 
PopClipRect()3452 void ImGui::PopClipRect()
3453 {
3454     ImGuiWindow* window = GetCurrentWindow();
3455     window->DrawList->PopClipRect();
3456     window->ClipRect = window->DrawList->_ClipRectStack.back();
3457 }
3458 
3459 // 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.
EndFrame()3460 void ImGui::EndFrame()
3461 {
3462     ImGuiContext& g = *GImGui;
3463     IM_ASSERT(g.Initialized);
3464     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
3465         return;
3466     IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()");
3467 
3468     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3469     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)
3470     {
3471         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3472         g.PlatformImeLastPos = g.PlatformImePos;
3473     }
3474 
3475     // Hide implicit "Debug" window if it hasn't been used
3476     IM_ASSERT(g.CurrentWindowStack.Size == 1);    // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name?
3477     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3478         g.CurrentWindow->Active = false;
3479     End();
3480 
3481     // Show CTRL+TAB list
3482     if (g.NavWindowingTarget)
3483         NavUpdateWindowingList();
3484 
3485     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3486     if (g.DragDropActive)
3487     {
3488         bool is_delivered = g.DragDropPayload.Delivery;
3489         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
3490         if (is_delivered || is_elapsed)
3491             ClearDragDrop();
3492     }
3493 
3494     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3495     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3496     {
3497         g.DragDropWithinSourceOrTarget = true;
3498         SetTooltip("...");
3499         g.DragDropWithinSourceOrTarget = false;
3500     }
3501 
3502     // Initiate moving window
3503     if (g.ActiveId == 0 && g.HoveredId == 0)
3504     {
3505         if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
3506         {
3507             // Click to focus window and start moving (after we're done with all our widgets)
3508             if (g.IO.MouseClicked[0])
3509             {
3510                 if (g.HoveredRootWindow != NULL)
3511                     StartMouseMovingWindow(g.HoveredWindow);
3512                 else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3513                     FocusWindow(NULL);  // Clicking on void disable focus
3514             }
3515 
3516             // With right mouse button we close popups without changing focus
3517             // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3518             if (g.IO.MouseClicked[1])
3519             {
3520                 // Find the top-most window between HoveredWindow and the front most Modal Window.
3521                 // This is where we can trim the popup stack.
3522                 ImGuiWindow* modal = GetFrontMostPopupModal();
3523                 bool hovered_window_above_modal = false;
3524                 if (modal == NULL)
3525                     hovered_window_above_modal = true;
3526                 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3527                 {
3528                     ImGuiWindow* window = g.Windows[i];
3529                     if (window == modal)
3530                         break;
3531                     if (window == g.HoveredWindow)
3532                         hovered_window_above_modal = true;
3533                 }
3534                 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
3535             }
3536         }
3537     }
3538 
3539     // Sort the window list so that all child windows are after their parent
3540     // We cannot do that on FocusWindow() because childs may not exist yet
3541     g.WindowsSortBuffer.resize(0);
3542     g.WindowsSortBuffer.reserve(g.Windows.Size);
3543     for (int i = 0; i != g.Windows.Size; i++)
3544     {
3545         ImGuiWindow* window = g.Windows[i];
3546         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
3547             continue;
3548         AddWindowToSortedBuffer(&g.WindowsSortBuffer, window);
3549     }
3550 
3551     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
3552     g.Windows.swap(g.WindowsSortBuffer);
3553     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3554 
3555     // Unlock font atlas
3556     g.IO.Fonts->Locked = false;
3557 
3558     // Clear Input data for next frame
3559     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3560     memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
3561     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
3562 
3563     g.FrameScopeActive = false;
3564     g.FrameCountEnded = g.FrameCount;
3565 }
3566 
Render()3567 void ImGui::Render()
3568 {
3569     ImGuiContext& g = *GImGui;
3570     IM_ASSERT(g.Initialized);
3571 
3572     if (g.FrameCountEnded != g.FrameCount)
3573         ImGui::EndFrame();
3574     g.FrameCountRendered = g.FrameCount;
3575 
3576     // Gather ImDrawList to render (for each active window)
3577     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3578     g.DrawDataBuilder.Clear();
3579     ImGuiWindow* windows_to_render_front_most[2];
3580     windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3581     windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3582     for (int n = 0; n != g.Windows.Size; n++)
3583     {
3584         ImGuiWindow* window = g.Windows[n];
3585         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3586             AddWindowToDrawDataSelectLayer(window);
3587     }
3588     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3589         if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3590             AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]);
3591     g.DrawDataBuilder.FlattenIntoSingleLayer();
3592 
3593     // Draw software mouse cursor if requested
3594     if (g.IO.MouseDrawCursor)
3595         RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor);
3596 
3597     if (!g.OverlayDrawList.VtxBuffer.empty())
3598         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
3599 
3600     // Setup ImDrawData structure for end-user
3601     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
3602     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3603     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3604 
3605     // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
3606 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3607     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3608         g.IO.RenderDrawListsFn(&g.DrawData);
3609 #endif
3610 }
3611 
3612 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3613 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)3614 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3615 {
3616     ImGuiContext& g = *GImGui;
3617 
3618     const char* text_display_end;
3619     if (hide_text_after_double_hash)
3620         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
3621     else
3622         text_display_end = text_end;
3623 
3624     ImFont* font = g.Font;
3625     const float font_size = g.FontSize;
3626     if (text == text_display_end)
3627         return ImVec2(0.0f, font_size);
3628     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3629 
3630     // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3631     const float font_scale = font_size / font->FontSize;
3632     const float character_spacing_x = 1.0f * font_scale;
3633     if (text_size.x > 0.0f)
3634         text_size.x -= character_spacing_x;
3635     text_size.x = (float)(int)(text_size.x + 0.95f);
3636 
3637     return text_size;
3638 }
3639 
3640 // Helper to calculate coarse clipping of large list of evenly sized items.
3641 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3642 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)3643 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3644 {
3645     ImGuiContext& g = *GImGui;
3646     ImGuiWindow* window = g.CurrentWindow;
3647     if (g.LogEnabled)
3648     {
3649         // If logging is active, do not perform any clipping
3650         *out_items_display_start = 0;
3651         *out_items_display_end = items_count;
3652         return;
3653     }
3654     if (window->SkipItems)
3655     {
3656         *out_items_display_start = *out_items_display_end = 0;
3657         return;
3658     }
3659 
3660     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3661     ImRect unclipped_rect = window->ClipRect;
3662     if (g.NavMoveRequest)
3663         unclipped_rect.Add(g.NavScoringRectScreen);
3664 
3665     const ImVec2 pos = window->DC.CursorPos;
3666     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3667     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3668 
3669     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
3670     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3671         start--;
3672     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3673         end++;
3674 
3675     start = ImClamp(start, 0, items_count);
3676     end = ImClamp(end + 1, start, items_count);
3677     *out_items_display_start = start;
3678     *out_items_display_end = end;
3679 }
3680 
3681 // Find window given position, search front-to-back
3682 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3683 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3684 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()3685 static void FindHoveredWindow()
3686 {
3687     ImGuiContext& g = *GImGui;
3688 
3689     ImGuiWindow* hovered_window = NULL;
3690     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs))
3691         hovered_window = g.MovingWindow;
3692 
3693     for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--)
3694     {
3695         ImGuiWindow* window = g.Windows[i];
3696         if (!window->Active || window->Hidden)
3697             continue;
3698         if (window->Flags & ImGuiWindowFlags_NoInputs)
3699             continue;
3700 
3701         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3702         ImRect bb(window->OuterRectClipped.Min - g.Style.TouchExtraPadding, window->OuterRectClipped.Max + g.Style.TouchExtraPadding);
3703         if (bb.Contains(g.IO.MousePos))
3704         {
3705             if (hovered_window == NULL)
3706                 hovered_window = window;
3707             if (hovered_window)
3708                 break;
3709         }
3710     }
3711 
3712     g.HoveredWindow = hovered_window;
3713     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3714 
3715 }
3716 
3717 // Test if mouse cursor is hovering given rectangle
3718 // NB- Rectangle is clipped by our current clip setting
3719 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
IsMouseHoveringRect(const ImVec2 & r_min,const ImVec2 & r_max,bool clip)3720 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3721 {
3722     ImGuiContext& g = *GImGui;
3723 
3724     // Clip
3725     ImRect rect_clipped(r_min, r_max);
3726     if (clip)
3727         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
3728 
3729     // Expand for touch input
3730     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3731     if (!rect_for_touch.Contains(g.IO.MousePos))
3732         return false;
3733     return true;
3734 }
3735 
GetKeyIndex(ImGuiKey imgui_key)3736 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3737 {
3738     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3739     return GImGui->IO.KeyMap[imgui_key];
3740 }
3741 
3742 // 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[]!
IsKeyDown(int user_key_index)3743 bool ImGui::IsKeyDown(int user_key_index)
3744 {
3745     if (user_key_index < 0) return false;
3746     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3747     return GImGui->IO.KeysDown[user_key_index];
3748 }
3749 
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)3750 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3751 {
3752     if (t == 0.0f)
3753         return 1;
3754     if (t <= repeat_delay || repeat_rate <= 0.0f)
3755         return 0;
3756     const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3757     return (count > 0) ? count : 0;
3758 }
3759 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)3760 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3761 {
3762     ImGuiContext& g = *GImGui;
3763     if (key_index < 0) return false;
3764     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3765     const float t = g.IO.KeysDownDuration[key_index];
3766     return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3767 }
3768 
IsKeyPressed(int user_key_index,bool repeat)3769 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3770 {
3771     ImGuiContext& g = *GImGui;
3772     if (user_key_index < 0) return false;
3773     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3774     const float t = g.IO.KeysDownDuration[user_key_index];
3775     if (t == 0.0f)
3776         return true;
3777     if (repeat && t > g.IO.KeyRepeatDelay)
3778         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
3779     return false;
3780 }
3781 
IsKeyReleased(int user_key_index)3782 bool ImGui::IsKeyReleased(int user_key_index)
3783 {
3784     ImGuiContext& g = *GImGui;
3785     if (user_key_index < 0) return false;
3786     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3787     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
3788 }
3789 
IsMouseDown(int button)3790 bool ImGui::IsMouseDown(int button)
3791 {
3792     ImGuiContext& g = *GImGui;
3793     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3794     return g.IO.MouseDown[button];
3795 }
3796 
IsAnyMouseDown()3797 bool ImGui::IsAnyMouseDown()
3798 {
3799     ImGuiContext& g = *GImGui;
3800     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
3801         if (g.IO.MouseDown[n])
3802             return true;
3803     return false;
3804 }
3805 
IsMouseClicked(int button,bool repeat)3806 bool ImGui::IsMouseClicked(int button, bool repeat)
3807 {
3808     ImGuiContext& g = *GImGui;
3809     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3810     const float t = g.IO.MouseDownDuration[button];
3811     if (t == 0.0f)
3812         return true;
3813 
3814     if (repeat && t > g.IO.KeyRepeatDelay)
3815     {
3816         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3817         if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3818             return true;
3819     }
3820 
3821     return false;
3822 }
3823 
IsMouseReleased(int button)3824 bool ImGui::IsMouseReleased(int button)
3825 {
3826     ImGuiContext& g = *GImGui;
3827     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3828     return g.IO.MouseReleased[button];
3829 }
3830 
IsMouseDoubleClicked(int button)3831 bool ImGui::IsMouseDoubleClicked(int button)
3832 {
3833     ImGuiContext& g = *GImGui;
3834     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3835     return g.IO.MouseDoubleClicked[button];
3836 }
3837 
IsMouseDragging(int button,float lock_threshold)3838 bool ImGui::IsMouseDragging(int button, float lock_threshold)
3839 {
3840     ImGuiContext& g = *GImGui;
3841     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3842     if (!g.IO.MouseDown[button])
3843         return false;
3844     if (lock_threshold < 0.0f)
3845         lock_threshold = g.IO.MouseDragThreshold;
3846     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3847 }
3848 
GetMousePos()3849 ImVec2 ImGui::GetMousePos()
3850 {
3851     return GImGui->IO.MousePos;
3852 }
3853 
3854 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()3855 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3856 {
3857     ImGuiContext& g = *GImGui;
3858     if (g.CurrentPopupStack.Size > 0)
3859         return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos;
3860     return g.IO.MousePos;
3861 }
3862 
3863 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
IsMousePosValid(const ImVec2 * mouse_pos)3864 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
3865 {
3866     if (mouse_pos == NULL)
3867         mouse_pos = &GImGui->IO.MousePos;
3868     const float MOUSE_INVALID = -256000.0f;
3869     return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
3870 }
3871 
3872 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(int button,float lock_threshold)3873 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3874 {
3875     ImGuiContext& g = *GImGui;
3876     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3877     if (lock_threshold < 0.0f)
3878         lock_threshold = g.IO.MouseDragThreshold;
3879     if (g.IO.MouseDown[button])
3880         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3881             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
3882     return ImVec2(0.0f, 0.0f);
3883 }
3884 
ResetMouseDragDelta(int button)3885 void ImGui::ResetMouseDragDelta(int button)
3886 {
3887     ImGuiContext& g = *GImGui;
3888     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3889     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3890     g.IO.MouseClickedPos[button] = g.IO.MousePos;
3891 }
3892 
GetMouseCursor()3893 ImGuiMouseCursor ImGui::GetMouseCursor()
3894 {
3895     return GImGui->MouseCursor;
3896 }
3897 
SetMouseCursor(ImGuiMouseCursor cursor_type)3898 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3899 {
3900     GImGui->MouseCursor = cursor_type;
3901 }
3902 
CaptureKeyboardFromApp(bool capture)3903 void ImGui::CaptureKeyboardFromApp(bool capture)
3904 {
3905     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
3906 }
3907 
CaptureMouseFromApp(bool capture)3908 void ImGui::CaptureMouseFromApp(bool capture)
3909 {
3910     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
3911 }
3912 
IsItemActive()3913 bool ImGui::IsItemActive()
3914 {
3915     ImGuiContext& g = *GImGui;
3916     if (g.ActiveId)
3917     {
3918         ImGuiWindow* window = g.CurrentWindow;
3919         return g.ActiveId == window->DC.LastItemId;
3920     }
3921     return false;
3922 }
3923 
IsItemDeactivated()3924 bool ImGui::IsItemDeactivated()
3925 {
3926     ImGuiContext& g = *GImGui;
3927     ImGuiWindow* window = g.CurrentWindow;
3928     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
3929 }
3930 
IsItemDeactivatedAfterEdit()3931 bool ImGui::IsItemDeactivatedAfterEdit()
3932 {
3933     ImGuiContext& g = *GImGui;
3934     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
3935 }
3936 
IsItemFocused()3937 bool ImGui::IsItemFocused()
3938 {
3939     ImGuiContext& g = *GImGui;
3940     return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
3941 }
3942 
IsItemClicked(int mouse_button)3943 bool ImGui::IsItemClicked(int mouse_button)
3944 {
3945     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
3946 }
3947 
IsAnyItemHovered()3948 bool ImGui::IsAnyItemHovered()
3949 {
3950     ImGuiContext& g = *GImGui;
3951     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
3952 }
3953 
IsAnyItemActive()3954 bool ImGui::IsAnyItemActive()
3955 {
3956     ImGuiContext& g = *GImGui;
3957     return g.ActiveId != 0;
3958 }
3959 
IsAnyItemFocused()3960 bool ImGui::IsAnyItemFocused()
3961 {
3962     ImGuiContext& g = *GImGui;
3963     return g.NavId != 0 && !g.NavDisableHighlight;
3964 }
3965 
IsItemVisible()3966 bool ImGui::IsItemVisible()
3967 {
3968     ImGuiWindow* window = GetCurrentWindowRead();
3969     return window->ClipRect.Overlaps(window->DC.LastItemRect);
3970 }
3971 
IsItemEdited()3972 bool ImGui::IsItemEdited()
3973 {
3974     ImGuiWindow* window = GetCurrentWindowRead();
3975     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
3976 }
3977 
3978 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()3979 void ImGui::SetItemAllowOverlap()
3980 {
3981     ImGuiContext& g = *GImGui;
3982     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
3983         g.HoveredIdAllowOverlap = true;
3984     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
3985         g.ActiveIdAllowOverlap = true;
3986 }
3987 
GetItemRectMin()3988 ImVec2 ImGui::GetItemRectMin()
3989 {
3990     ImGuiWindow* window = GetCurrentWindowRead();
3991     return window->DC.LastItemRect.Min;
3992 }
3993 
GetItemRectMax()3994 ImVec2 ImGui::GetItemRectMax()
3995 {
3996     ImGuiWindow* window = GetCurrentWindowRead();
3997     return window->DC.LastItemRect.Max;
3998 }
3999 
GetItemRectSize()4000 ImVec2 ImGui::GetItemRectSize()
4001 {
4002     ImGuiWindow* window = GetCurrentWindowRead();
4003     return window->DC.LastItemRect.GetSize();
4004 }
4005 
GetViewportRect()4006 static ImRect GetViewportRect()
4007 {
4008     ImGuiContext& g = *GImGui;
4009     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4010         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4011     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4012 }
4013 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4014 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4015 {
4016     ImGuiContext& g = *GImGui;
4017     ImGuiWindow* parent_window = g.CurrentWindow;
4018 
4019     flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4020     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4021 
4022     // Size
4023     const ImVec2 content_avail = GetContentRegionAvail();
4024     ImVec2 size = ImFloor(size_arg);
4025     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4026     if (size.x <= 0.0f)
4027         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4028     if (size.y <= 0.0f)
4029         size.y = ImMax(content_avail.y + size.y, 4.0f);
4030     SetNextWindowSize(size);
4031 
4032     // Name
4033     char title[256];
4034     if (name)
4035         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", parent_window->Name, name);
4036     else
4037         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4038 
4039     const float backup_border_size = g.Style.ChildBorderSize;
4040     if (!border)
4041         g.Style.ChildBorderSize = 0.0f;
4042     bool ret = Begin(title, NULL, flags);
4043     g.Style.ChildBorderSize = backup_border_size;
4044 
4045     ImGuiWindow* child_window = g.CurrentWindow;
4046     child_window->ChildId = id;
4047     child_window->AutoFitChildAxises = auto_fit_axises;
4048 
4049     // Process navigation-in immediately so NavInit can run on first frame
4050     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4051     {
4052         FocusWindow(child_window);
4053         NavInitWindow(child_window, false);
4054         SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4055         g.ActiveIdSource = ImGuiInputSource_Nav;
4056     }
4057     return ret;
4058 }
4059 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4060 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4061 {
4062     ImGuiWindow* window = GetCurrentWindow();
4063     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4064 }
4065 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4066 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4067 {
4068     IM_ASSERT(id != 0);
4069     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4070 }
4071 
EndChild()4072 void ImGui::EndChild()
4073 {
4074     ImGuiContext& g = *GImGui;
4075     ImGuiWindow* window = g.CurrentWindow;
4076 
4077     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
4078     if (window->BeginCount > 1)
4079     {
4080         End();
4081     }
4082     else
4083     {
4084         ImVec2 sz = window->Size;
4085         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4086             sz.x = ImMax(4.0f, sz.x);
4087         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4088             sz.y = ImMax(4.0f, sz.y);
4089         End();
4090 
4091         ImGuiWindow* parent_window = g.CurrentWindow;
4092         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4093         ItemSize(sz);
4094         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4095         {
4096             ItemAdd(bb, window->ChildId);
4097             RenderNavHighlight(bb, window->ChildId);
4098 
4099             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4100             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4101                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4102         }
4103         else
4104         {
4105             // Not navigable into
4106             ItemAdd(bb, 0);
4107         }
4108     }
4109 }
4110 
4111 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4112 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4113 {
4114     ImGuiContext& g = *GImGui;
4115     const ImGuiStyle& style = g.Style;
4116     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4117     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4118     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4119     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4120     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4121     PopStyleVar(3);
4122     PopStyleColor();
4123     return ret;
4124 }
4125 
EndChildFrame()4126 void ImGui::EndChildFrame()
4127 {
4128     EndChild();
4129 }
4130 
4131 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4132 static void CheckStacksSize(ImGuiWindow* window, bool write)
4133 {
4134     // 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)
4135     ImGuiContext& g = *GImGui;
4136     int* p_backup = &window->DC.StackSizesBackup[0];
4137     { 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()
4138     { 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()
4139     { 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()
4140     // 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.
4141     { 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()
4142     { 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()
4143     { 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()
4144     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4145 }
4146 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4147 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4148 {
4149     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4150     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4151     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4152 }
4153 
FindWindowByName(const char * name)4154 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4155 {
4156     ImGuiContext& g = *GImGui;
4157     ImGuiID id = ImHash(name, 0);
4158     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4159 }
4160 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4161 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4162 {
4163     ImGuiContext& g = *GImGui;
4164 
4165     // Create window the first time
4166     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4167     window->Flags = flags;
4168     g.WindowsById.SetVoidPtr(window->ID, window);
4169 
4170     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4171     window->Pos = ImVec2(60, 60);
4172 
4173     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4174     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4175         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4176         {
4177             // Retrieve settings from .ini file
4178             window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
4179             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4180             window->Pos = ImFloor(settings->Pos);
4181             window->Collapsed = settings->Collapsed;
4182             if (ImLengthSqr(settings->Size) > 0.00001f)
4183                 size = ImFloor(settings->Size);
4184         }
4185     window->Size = window->SizeFull = window->SizeFullAtLastBegin = size;
4186     window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4187 
4188     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4189     {
4190         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4191         window->AutoFitOnlyGrows = false;
4192     }
4193     else
4194     {
4195         if (window->Size.x <= 0.0f)
4196             window->AutoFitFramesX = 2;
4197         if (window->Size.y <= 0.0f)
4198             window->AutoFitFramesY = 2;
4199         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4200     }
4201 
4202     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4203         g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
4204     else
4205         g.Windows.push_back(window);
4206     return window;
4207 }
4208 
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4209 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4210 {
4211     ImGuiContext& g = *GImGui;
4212     if (g.NextWindowData.SizeConstraintCond != 0)
4213     {
4214         // Using -1,-1 on either X/Y axis to preserve the current size.
4215         ImRect cr = g.NextWindowData.SizeConstraintRect;
4216         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4217         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4218         if (g.NextWindowData.SizeCallback)
4219         {
4220             ImGuiSizeCallbackData data;
4221             data.UserData = g.NextWindowData.SizeCallbackUserData;
4222             data.Pos = window->Pos;
4223             data.CurrentSize = window->SizeFull;
4224             data.DesiredSize = new_size;
4225             g.NextWindowData.SizeCallback(&data);
4226             new_size = data.DesiredSize;
4227         }
4228     }
4229 
4230     // Minimum size
4231     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4232     {
4233         new_size = ImMax(new_size, g.Style.WindowMinSize);
4234         new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4235     }
4236     return new_size;
4237 }
4238 
CalcSizeContents(ImGuiWindow * window)4239 static ImVec2 CalcSizeContents(ImGuiWindow* window)
4240 {
4241     ImVec2 sz;
4242     sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4243     sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4244     return sz + window->WindowPadding;
4245 }
4246 
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)4247 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4248 {
4249     ImGuiContext& g = *GImGui;
4250     ImGuiStyle& style = g.Style;
4251     if (window->Flags & ImGuiWindowFlags_Tooltip)
4252     {
4253         // Tooltip always resize
4254         return size_contents;
4255     }
4256     else
4257     {
4258         // 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.
4259         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4260         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4261         ImVec2 size_min = style.WindowMinSize;
4262         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)
4263             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4264         ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4265         ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
4266         if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4267             size_auto_fit.y += style.ScrollbarSize;
4268         if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4269             size_auto_fit.x += style.ScrollbarSize;
4270         return size_auto_fit;
4271     }
4272 }
4273 
CalcWindowExpectedSize(ImGuiWindow * window)4274 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4275 {
4276     ImVec2 size_contents = CalcSizeContents(window);
4277     return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents));
4278 }
4279 
GetScrollMaxX(ImGuiWindow * window)4280 static float GetScrollMaxX(ImGuiWindow* window)
4281 {
4282     return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4283 }
4284 
GetScrollMaxY(ImGuiWindow * window)4285 static float GetScrollMaxY(ImGuiWindow* window)
4286 {
4287     return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4288 }
4289 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)4290 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4291 {
4292     ImGuiContext& g = *GImGui;
4293     ImVec2 scroll = window->Scroll;
4294     if (window->ScrollTarget.x < FLT_MAX)
4295     {
4296         float cr_x = window->ScrollTargetCenterRatio.x;
4297         scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4298     }
4299     if (window->ScrollTarget.y < FLT_MAX)
4300     {
4301         // '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.
4302         float cr_y = window->ScrollTargetCenterRatio.y;
4303         float target_y = window->ScrollTarget.y;
4304         if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4305             target_y = 0.0f;
4306         if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4307             target_y = window->SizeContents.y;
4308         scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4309     }
4310     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4311     if (!window->Collapsed && !window->SkipItems)
4312     {
4313         scroll.x = ImMin(scroll.x, GetScrollMaxX(window));
4314         scroll.y = ImMin(scroll.y, GetScrollMaxY(window));
4315     }
4316     return scroll;
4317 }
4318 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4319 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4320 {
4321     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4322         return ImGuiCol_PopupBg;
4323     if (flags & ImGuiWindowFlags_ChildWindow)
4324         return ImGuiCol_ChildBg;
4325     return ImGuiCol_WindowBg;
4326 }
4327 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4328 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4329 {
4330     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
4331     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4332     ImVec2 size_expected = pos_max - pos_min;
4333     ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
4334     *out_pos = pos_min;
4335     if (corner_norm.x == 0.0f)
4336         out_pos->x -= (size_constrained.x - size_expected.x);
4337     if (corner_norm.y == 0.0f)
4338         out_pos->y -= (size_constrained.y - size_expected.y);
4339     *out_size = size_constrained;
4340 }
4341 
4342 struct ImGuiResizeGripDef
4343 {
4344     ImVec2  CornerPos;
4345     ImVec2  InnerDir;
4346     int     AngleMin12, AngleMax12;
4347 };
4348 
4349 const ImGuiResizeGripDef resize_grip_def[4] =
4350 {
4351     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4352     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4353     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4354     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4355 };
4356 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4357 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4358 {
4359     ImRect rect = window->Rect();
4360     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4361     if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y,                rect.Max.x - perp_padding, rect.Min.y + thickness);
4362     if (border_n == 1) return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x,                rect.Max.y - perp_padding);
4363     if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y);
4364     if (border_n == 3) return ImRect(rect.Min.x,                rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding);
4365     IM_ASSERT(0);
4366     return ImRect();
4367 }
4368 
4369 // Handle resize for: Resize Grips, Borders, Gamepad
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])4370 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4371 {
4372     ImGuiContext& g = *GImGui;
4373     ImGuiWindowFlags flags = window->Flags;
4374     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4375         return;
4376 
4377     const int resize_border_count = g.IO.ConfigResizeWindowsFromEdges ? 4 : 0;
4378     const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4379     const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f);
4380 
4381     ImVec2 pos_target(FLT_MAX, FLT_MAX);
4382     ImVec2 size_target(FLT_MAX, FLT_MAX);
4383 
4384     // Manual resize grips
4385     PushID("#RESIZE");
4386     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4387     {
4388         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4389         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4390 
4391         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4392         ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size);
4393         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4394         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4395         bool hovered, held;
4396         ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4397         if (hovered || held)
4398             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4399 
4400         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4401         {
4402             // Manual auto-fit when double-clicking
4403             size_target = CalcSizeAfterConstraint(window, size_auto_fit);
4404             ClearActiveID();
4405         }
4406         else if (held)
4407         {
4408             // Resize from any of the four corners
4409             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4410             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip
4411             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
4412         }
4413         if (resize_grip_n == 0 || held || hovered)
4414             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4415     }
4416     for (int border_n = 0; border_n < resize_border_count; border_n++)
4417     {
4418         const float BORDER_SIZE = 5.0f;          // FIXME: Only works _inside_ window because of HoveredWindow check.
4419         const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise
4420         bool hovered, held;
4421         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_size, BORDER_SIZE);
4422         ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4423         if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held)
4424         {
4425             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4426             if (held) *border_held = border_n;
4427         }
4428         if (held)
4429         {
4430             ImVec2 border_target = window->Pos;
4431             ImVec2 border_posn;
4432             if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); }
4433             if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); }
4434             if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); }
4435             if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); }
4436             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4437         }
4438     }
4439     PopID();
4440 
4441     // Navigation resize (keyboard/gamepad)
4442     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4443     {
4444         ImVec2 nav_resize_delta;
4445         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4446             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4447         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4448             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4449         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4450         {
4451             const float NAV_RESIZE_SPEED = 600.0f;
4452             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4453             g.NavWindowingToggleLayer = false;
4454             g.NavDisableMouseHover = true;
4455             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4456             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4457             size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4458         }
4459     }
4460 
4461     // Apply back modified position/size to window
4462     if (size_target.x != FLT_MAX)
4463     {
4464         window->SizeFull = size_target;
4465         MarkIniSettingsDirty(window);
4466     }
4467     if (pos_target.x != FLT_MAX)
4468     {
4469         window->Pos = ImFloor(pos_target);
4470         MarkIniSettingsDirty(window);
4471     }
4472 
4473     window->Size = window->SizeFull;
4474 }
4475 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)4476 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4477 {
4478     window->ParentWindow = parent_window;
4479     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4480     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4481         window->RootWindow = parent_window->RootWindow;
4482     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4483         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4484     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4485         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4486 }
4487 
4488 // Push a new ImGui window to add widgets to.
4489 // - 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.
4490 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4491 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4492 //   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.
4493 // - 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.
4494 // - 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.
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)4495 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4496 {
4497     ImGuiContext& g = *GImGui;
4498     const ImGuiStyle& style = g.Style;
4499     IM_ASSERT(name != NULL);                        // Window name required
4500     IM_ASSERT(g.FrameScopeActive);                  // Forgot to call ImGui::NewFrame()
4501     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4502 
4503     // Find or create
4504     ImGuiWindow* window = FindWindowByName(name);
4505     const bool window_just_created = (window == NULL);
4506     if (window_just_created)
4507     {
4508         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.
4509         window = CreateNewWindow(name, size_on_first_use, flags);
4510     }
4511 
4512     // Automatically disable manual moving/resizing when NoInputs is set
4513     if (flags & ImGuiWindowFlags_NoInputs)
4514         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4515 
4516     if (flags & ImGuiWindowFlags_NavFlattened)
4517         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4518 
4519     const int current_frame = g.FrameCount;
4520     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4521     if (first_begin_of_the_frame)
4522         window->Flags = (ImGuiWindowFlags)flags;
4523     else
4524         flags = window->Flags;
4525 
4526     // 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
4527     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4528     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4529     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4530     window->HasCloseButton = (p_open != NULL);
4531 
4532     // Update the Appearing flag
4533     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4534     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4535     if (flags & ImGuiWindowFlags_Popup)
4536     {
4537         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4538         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4539         window_just_activated_by_user |= (window != popup_ref.Window);
4540     }
4541     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4542     if (window->Appearing)
4543         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4544 
4545     // Add to stack
4546     g.CurrentWindowStack.push_back(window);
4547     SetCurrentWindow(window);
4548     CheckStacksSize(window, true);
4549     if (flags & ImGuiWindowFlags_Popup)
4550     {
4551         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4552         popup_ref.Window = window;
4553         g.CurrentPopupStack.push_back(popup_ref);
4554         window->PopupId = popup_ref.PopupId;
4555     }
4556 
4557     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4558         window->NavLastIds[0] = 0;
4559 
4560     // Process SetNextWindow***() calls
4561     bool window_pos_set_by_api = false;
4562     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4563     if (g.NextWindowData.PosCond)
4564     {
4565         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4566         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
4567         {
4568             // May be processed on the next frame if this is our first frame and we are measuring size
4569             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4570             window->SetWindowPosVal = g.NextWindowData.PosVal;
4571             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4572             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4573         }
4574         else
4575         {
4576             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
4577         }
4578     }
4579     if (g.NextWindowData.SizeCond)
4580     {
4581         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4582         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4583         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
4584     }
4585     if (g.NextWindowData.ContentSizeCond)
4586     {
4587         // Adjust passed "client size" to become a "window size"
4588         window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4589         if (window->SizeContentsExplicit.y != 0.0f)
4590             window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4591     }
4592     else if (first_begin_of_the_frame)
4593     {
4594         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4595     }
4596     if (g.NextWindowData.CollapsedCond)
4597         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
4598     if (g.NextWindowData.FocusCond)
4599         FocusWindow(window);
4600     if (window->Appearing)
4601         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4602 
4603     // When reusing window again multiple times a frame, just append content (don't need to setup again)
4604     if (first_begin_of_the_frame)
4605     {
4606         // Initialize
4607         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4608         UpdateWindowParentAndRootLinks(window, flags, parent_window);
4609 
4610         window->Active = true;
4611         window->BeginOrderWithinParent = 0;
4612         window->BeginOrderWithinContext = g.WindowsActiveCount++;
4613         window->BeginCount = 0;
4614         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4615         window->LastFrameActive = current_frame;
4616         window->IDStack.resize(1);
4617 
4618         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
4619 
4620         // Update contents size from last frame for auto-fitting (or use explicit size)
4621         window->SizeContents = CalcSizeContents(window);
4622         if (window->HiddenFramesRegular > 0)
4623             window->HiddenFramesRegular--;
4624         if (window->HiddenFramesForResize > 0)
4625             window->HiddenFramesForResize--;
4626 
4627         // Hide new windows for one frame until they calculate their size
4628         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
4629             window->HiddenFramesForResize = 1;
4630 
4631         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
4632         // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
4633         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
4634         {
4635             window->HiddenFramesForResize = 1;
4636             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4637             {
4638                 if (!window_size_x_set_by_api)
4639                     window->Size.x = window->SizeFull.x = 0.f;
4640                 if (!window_size_y_set_by_api)
4641                     window->Size.y = window->SizeFull.y = 0.f;
4642                 window->SizeContents = ImVec2(0.f, 0.f);
4643             }
4644         }
4645 
4646         SetCurrentWindow(window);
4647 
4648         // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
4649         window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4650         window->WindowPadding = style.WindowPadding;
4651         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4652             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4653         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
4654         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
4655 
4656         // Collapse window by double-clicking on title bar
4657         // 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
4658         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4659         {
4660             // 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.
4661             ImRect title_bar_rect = window->TitleBarRect();
4662             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4663                 window->WantCollapseToggle = true;
4664             if (window->WantCollapseToggle)
4665             {
4666                 window->Collapsed = !window->Collapsed;
4667                 MarkIniSettingsDirty(window);
4668                 FocusWindow(window);
4669             }
4670         }
4671         else
4672         {
4673             window->Collapsed = false;
4674         }
4675         window->WantCollapseToggle = false;
4676 
4677         // SIZE
4678 
4679         // Calculate auto-fit size, handle automatic resize
4680         const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
4681         ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
4682         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
4683         {
4684             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
4685             if (!window_size_x_set_by_api)
4686                 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
4687             if (!window_size_y_set_by_api)
4688                 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
4689         }
4690         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4691         {
4692             // Auto-fit may only grow window during the first few frames
4693             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4694             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
4695                 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4696             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
4697                 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4698             if (!window->Collapsed)
4699                 MarkIniSettingsDirty(window);
4700         }
4701 
4702         // Apply minimum/maximum window size constraints and final size
4703         window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
4704         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
4705 
4706         // SCROLLBAR STATUS
4707 
4708         // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
4709         if (!window->Collapsed)
4710         {
4711             // When reading the current size we need to read it after size constraints have been applied
4712             float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
4713             float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
4714             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
4715             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4716             if (window->ScrollbarX && !window->ScrollbarY)
4717                 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4718             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4719         }
4720 
4721         // POSITION
4722 
4723         // Popup latch its initial position, will position itself when it appears next frame
4724         if (window_just_activated_by_user)
4725         {
4726             window->AutoPosLastDirection = ImGuiDir_None;
4727             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4728                 window->Pos = g.CurrentPopupStack.back().OpenPopupPos;
4729         }
4730 
4731         // Position child window
4732         if (flags & ImGuiWindowFlags_ChildWindow)
4733         {
4734             window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
4735             parent_window->DC.ChildWindows.push_back(window);
4736             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
4737                 window->Pos = parent_window->DC.CursorPos;
4738         }
4739 
4740         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
4741         if (window_pos_with_pivot)
4742             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
4743         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
4744             window->Pos = FindBestWindowPosForPopup(window);
4745         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
4746             window->Pos = FindBestWindowPosForPopup(window);
4747         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
4748             window->Pos = FindBestWindowPosForPopup(window);
4749 
4750         // Clamp position so it stays visible
4751         if (!(flags & ImGuiWindowFlags_ChildWindow))
4752         {
4753             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.
4754             {
4755                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
4756                 window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size;
4757                 window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
4758             }
4759         }
4760         window->Pos = ImFloor(window->Pos);
4761 
4762         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
4763         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
4764 
4765         // Prepare for item focus requests
4766         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4767         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4768         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4769         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4770 
4771         // Apply scrolling
4772         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
4773         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4774 
4775         // Apply window focus (new and reactivated windows are moved to front)
4776         bool want_focus = false;
4777         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4778             if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4779                 want_focus = true;
4780 
4781         // Handle manual resize: Resize Grips, Borders, Gamepad
4782         int border_held = -1;
4783         ImU32 resize_grip_col[4] = { 0 };
4784         const int resize_grip_count = g.IO.ConfigResizeWindowsFromEdges ? 2 : 1; // 4
4785         const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4786         if (!window->Collapsed)
4787             UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
4788 
4789         // Default item width. Make it proportional to window size if window manually resizes
4790         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4791             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4792         else
4793             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4794 
4795         // DRAWING
4796 
4797         // Setup draw list and outer clipping rectangle
4798         window->DrawList->Clear();
4799         window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
4800         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
4801         ImRect viewport_rect(GetViewportRect());
4802         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
4803             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
4804         else
4805             PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
4806 
4807         // Draw modal window background (darkens what is behind them, all viewports)
4808         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
4809         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
4810         if (dim_bg_for_modal || dim_bg_for_window_list)
4811         {
4812             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4813             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
4814         }
4815 
4816         // Draw navigation selection/windowing rectangle background
4817         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
4818         {
4819             ImRect bb = window->Rect();
4820             bb.Expand(g.FontSize);
4821             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
4822                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
4823         }
4824 
4825         // Draw window + handle manual resize
4826         const float window_rounding = window->WindowRounding;
4827         const float window_border_size = window->WindowBorderSize;
4828         const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
4829         const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
4830         const ImRect title_bar_rect = window->TitleBarRect();
4831         if (window->Collapsed)
4832         {
4833             // Title bar only
4834             float backup_border_size = style.FrameBorderSize;
4835             g.Style.FrameBorderSize = window->WindowBorderSize;
4836             ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
4837             RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
4838             g.Style.FrameBorderSize = backup_border_size;
4839         }
4840         else
4841         {
4842             // Window background
4843             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
4844             if (g.NextWindowData.BgAlphaCond != 0)
4845             {
4846                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
4847                 g.NextWindowData.BgAlphaCond = 0;
4848             }
4849             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
4850 
4851             // Title bar
4852             ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
4853             if (!(flags & ImGuiWindowFlags_NoTitleBar))
4854                 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
4855 
4856             // Menu bar
4857             if (flags & ImGuiWindowFlags_MenuBar)
4858             {
4859                 ImRect menu_bar_rect = window->MenuBarRect();
4860                 menu_bar_rect.ClipWith(window->Rect());  // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
4861                 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
4862                 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
4863                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4864             }
4865 
4866             // Scrollbars
4867             if (window->ScrollbarX)
4868                 Scrollbar(ImGuiLayoutType_Horizontal);
4869             if (window->ScrollbarY)
4870                 Scrollbar(ImGuiLayoutType_Vertical);
4871 
4872             // Render resize grips (after their input handling so we don't have a frame of latency)
4873             if (!(flags & ImGuiWindowFlags_NoResize))
4874             {
4875                 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4876                 {
4877                     const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4878                     const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4879                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
4880                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
4881                     window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
4882                     window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
4883                 }
4884             }
4885 
4886             // Borders
4887             if (window_border_size > 0.0f)
4888                 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
4889             if (border_held != -1)
4890             {
4891                 ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f);
4892                 window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
4893             }
4894             if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
4895                 window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4896         }
4897 
4898         // Draw navigation selection/windowing rectangle border
4899         if (g.NavWindowingTargetAnim == window)
4900         {
4901             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
4902             ImRect bb = window->Rect();
4903             bb.Expand(g.FontSize);
4904             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
4905             {
4906                 bb.Expand(-g.FontSize - 1.0f);
4907                 rounding = window->WindowRounding;
4908             }
4909             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
4910         }
4911 
4912         // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
4913         window->SizeFullAtLastBegin = window->SizeFull;
4914 
4915         // Update various regions. Variables they depends on are set above in this function.
4916         // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
4917         window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
4918         window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
4919         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));
4920         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));
4921 
4922         // Setup drawing context
4923         // (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.)
4924         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
4925         window->DC.GroupOffset.x = 0.0f;
4926         window->DC.ColumnsOffset.x = 0.0f;
4927         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4928         window->DC.CursorPos = window->DC.CursorStartPos;
4929         window->DC.CursorPosPrevLine = window->DC.CursorPos;
4930         window->DC.CursorMaxPos = window->DC.CursorStartPos;
4931         window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
4932         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4933         window->DC.NavHideHighlightOneFrame = false;
4934         window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f);
4935         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
4936         window->DC.NavLayerActiveMaskNext = 0x00;
4937         window->DC.MenuBarAppending = false;
4938         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4939         window->DC.ChildWindows.resize(0);
4940         window->DC.LayoutType = ImGuiLayoutType_Vertical;
4941         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
4942         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
4943         window->DC.ItemWidth = window->ItemWidthDefault;
4944         window->DC.TextWrapPos = -1.0f; // disabled
4945         window->DC.ItemFlagsStack.resize(0);
4946         window->DC.ItemWidthStack.resize(0);
4947         window->DC.TextWrapPosStack.resize(0);
4948         window->DC.ColumnsSet = NULL;
4949         window->DC.TreeDepth = 0;
4950         window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
4951         window->DC.StateStorage = &window->StateStorage;
4952         window->DC.GroupStack.resize(0);
4953         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
4954 
4955         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
4956         {
4957             window->DC.ItemFlags = parent_window->DC.ItemFlags;
4958             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
4959         }
4960 
4961         if (window->AutoFitFramesX > 0)
4962             window->AutoFitFramesX--;
4963         if (window->AutoFitFramesY > 0)
4964             window->AutoFitFramesY--;
4965 
4966         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
4967         if (want_focus)
4968         {
4969             FocusWindow(window);
4970             NavInitWindow(window, false);
4971         }
4972 
4973         // Title bar
4974         if (!(flags & ImGuiWindowFlags_NoTitleBar))
4975         {
4976             // Close & collapse button are on layer 1 (same as menus) and don't default focus
4977             const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
4978             window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
4979             window->DC.NavLayerCurrent++;
4980             window->DC.NavLayerCurrentMask <<= 1;
4981 
4982             // Collapse button
4983             if (!(flags & ImGuiWindowFlags_NoCollapse))
4984                 if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos))
4985                     window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
4986 
4987             // Close button
4988             if (p_open != NULL)
4989             {
4990                 const float pad = style.FramePadding.y;
4991                 const float rad = g.FontSize * 0.5f;
4992                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
4993                     *p_open = false;
4994             }
4995 
4996             window->DC.NavLayerCurrent--;
4997             window->DC.NavLayerCurrentMask >>= 1;
4998             window->DC.ItemFlags = item_flags_backup;
4999 
5000             // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.)
5001             ImVec2 text_size = CalcTextSize(name, NULL, true);
5002             ImRect text_r = title_bar_rect;
5003             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5004             float pad_right = (p_open == NULL)                     ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5005             if (style.WindowTitleAlign.x > 0.0f)
5006                 pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
5007             text_r.Min.x += pad_left;
5008             text_r.Max.x -= pad_right;
5009             ImRect clip_rect = text_r;
5010             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()
5011             RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
5012         }
5013 
5014         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5015         window->OuterRectClipped = window->Rect();
5016         window->OuterRectClipped.ClipWith(window->ClipRect);
5017 
5018         // Pressing CTRL+C while holding on a window copy its content to the clipboard
5019         // 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.
5020         // Maybe we can support CTRL+C on every element?
5021         /*
5022         if (g.ActiveId == move_id)
5023             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5024                 ImGui::LogToClipboard();
5025         */
5026 
5027         // Inner rectangle
5028         // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5029         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5030         window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5031         window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5032         window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5033         window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5034         //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5035 
5036         // Inner clipping rectangle
5037         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5038         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5039         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
5040         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5041         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
5042 
5043         // 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.).
5044         window->DC.LastItemId = window->MoveId;
5045         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5046         window->DC.LastItemRect = title_bar_rect;
5047     }
5048 
5049     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5050 
5051     // 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)
5052     if (first_begin_of_the_frame)
5053         window->WriteAccessed = false;
5054 
5055     window->BeginCount++;
5056     g.NextWindowData.Clear();
5057 
5058     if (flags & ImGuiWindowFlags_ChildWindow)
5059     {
5060         // Child window can be out of sight and have "negative" clip windows.
5061         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5062         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5063 
5064         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5065             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5066                 window->HiddenFramesRegular = 1;
5067 
5068         // Completely hide along with parent or if parent is collapsed
5069         if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5070             window->HiddenFramesRegular = 1;
5071     }
5072 
5073     // 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)
5074     if (style.Alpha <= 0.0f)
5075         window->HiddenFramesRegular = 1;
5076 
5077     // Update the Hidden flag
5078     window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize);
5079 
5080     // Return false if we don't intend to display anything to allow user to perform an early out optimization
5081     window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5082 
5083     return !window->SkipItems;
5084 }
5085 
5086 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5087 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)5088 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5089 {
5090     // 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.
5091     if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5092         ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
5093 
5094     // Old API feature: override the window background alpha with a parameter.
5095     if (bg_alpha_override >= 0.0f)
5096         ImGui::SetNextWindowBgAlpha(bg_alpha_override);
5097 
5098     return ImGui::Begin(name, p_open, flags);
5099 }
5100 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5101 
End()5102 void ImGui::End()
5103 {
5104     ImGuiContext& g = *GImGui;
5105     ImGuiWindow* window = g.CurrentWindow;
5106 
5107     if (window->DC.ColumnsSet != NULL)
5108         EndColumns();
5109     PopClipRect();   // Inner window clip rectangle
5110 
5111     // Stop logging
5112     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
5113         LogFinish();
5114 
5115     // Pop from window stack
5116     g.CurrentWindowStack.pop_back();
5117     if (window->Flags & ImGuiWindowFlags_Popup)
5118         g.CurrentPopupStack.pop_back();
5119     CheckStacksSize(window, false);
5120     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5121 }
5122 
BringWindowToFront(ImGuiWindow * window)5123 void ImGui::BringWindowToFront(ImGuiWindow* window)
5124 {
5125     ImGuiContext& g = *GImGui;
5126     ImGuiWindow* current_front_window = g.Windows.back();
5127     if (current_front_window == window || current_front_window->RootWindow == window)
5128         return;
5129     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5130         if (g.Windows[i] == window)
5131         {
5132             g.Windows.erase(g.Windows.Data + i);
5133             g.Windows.push_back(window);
5134             break;
5135         }
5136 }
5137 
BringWindowToBack(ImGuiWindow * window)5138 void ImGui::BringWindowToBack(ImGuiWindow* window)
5139 {
5140     ImGuiContext& g = *GImGui;
5141     if (g.Windows[0] == window)
5142         return;
5143     for (int i = 0; i < g.Windows.Size; i++)
5144         if (g.Windows[i] == window)
5145         {
5146             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5147             g.Windows[0] = window;
5148             break;
5149         }
5150 }
5151 
5152 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5153 void ImGui::FocusWindow(ImGuiWindow* window)
5154 {
5155     ImGuiContext& g = *GImGui;
5156 
5157     if (g.NavWindow != window)
5158     {
5159         g.NavWindow = window;
5160         if (window && g.NavDisableMouseHover)
5161             g.NavMousePosDirty = true;
5162         g.NavInitRequest = false;
5163         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5164         g.NavIdIsAlive = false;
5165         g.NavLayer = 0;
5166         //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
5167     }
5168 
5169     // Passing NULL allow to disable keyboard focus
5170     if (!window)
5171         return;
5172 
5173     // Move the root window to the top of the pile
5174     if (window->RootWindow)
5175         window = window->RootWindow;
5176 
5177     // Steal focus on active widgets
5178     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5179         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5180             ClearActiveID();
5181 
5182     // Bring to front
5183     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5184         BringWindowToFront(window);
5185 }
5186 
FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow * ignore_window)5187 void ImGui::FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window)
5188 {
5189     ImGuiContext& g = *GImGui;
5190     for (int i = g.Windows.Size - 1; i >= 0; i--)
5191         if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
5192         {
5193             ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]);
5194             FocusWindow(focus_window);
5195             return;
5196         }
5197 }
5198 
PushItemWidth(float item_width)5199 void ImGui::PushItemWidth(float item_width)
5200 {
5201     ImGuiWindow* window = GetCurrentWindow();
5202     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5203     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5204 }
5205 
PushMultiItemsWidths(int components,float w_full)5206 void ImGui::PushMultiItemsWidths(int components, float w_full)
5207 {
5208     ImGuiWindow* window = GetCurrentWindow();
5209     const ImGuiStyle& style = GImGui->Style;
5210     if (w_full <= 0.0f)
5211         w_full = CalcItemWidth();
5212     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5213     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5214     window->DC.ItemWidthStack.push_back(w_item_last);
5215     for (int i = 0; i < components-1; i++)
5216         window->DC.ItemWidthStack.push_back(w_item_one);
5217     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5218 }
5219 
PopItemWidth()5220 void ImGui::PopItemWidth()
5221 {
5222     ImGuiWindow* window = GetCurrentWindow();
5223     window->DC.ItemWidthStack.pop_back();
5224     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5225 }
5226 
CalcItemWidth()5227 float ImGui::CalcItemWidth()
5228 {
5229     ImGuiWindow* window = GetCurrentWindowRead();
5230     float w = window->DC.ItemWidth;
5231     if (w < 0.0f)
5232     {
5233         // 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.
5234         float width_to_right_edge = GetContentRegionAvail().x;
5235         w = ImMax(1.0f, width_to_right_edge + w);
5236     }
5237     w = (float)(int)w;
5238     return w;
5239 }
5240 
SetCurrentFont(ImFont * font)5241 void ImGui::SetCurrentFont(ImFont* font)
5242 {
5243     ImGuiContext& g = *GImGui;
5244     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5245     IM_ASSERT(font->Scale > 0.0f);
5246     g.Font = font;
5247     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
5248     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5249 
5250     ImFontAtlas* atlas = g.Font->ContainerAtlas;
5251     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5252     g.DrawListSharedData.Font = g.Font;
5253     g.DrawListSharedData.FontSize = g.FontSize;
5254 }
5255 
PushFont(ImFont * font)5256 void ImGui::PushFont(ImFont* font)
5257 {
5258     ImGuiContext& g = *GImGui;
5259     if (!font)
5260         font = GetDefaultFont();
5261     SetCurrentFont(font);
5262     g.FontStack.push_back(font);
5263     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
5264 }
5265 
PopFont()5266 void  ImGui::PopFont()
5267 {
5268     ImGuiContext& g = *GImGui;
5269     g.CurrentWindow->DrawList->PopTextureID();
5270     g.FontStack.pop_back();
5271     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5272 }
5273 
PushItemFlag(ImGuiItemFlags option,bool enabled)5274 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5275 {
5276     ImGuiWindow* window = GetCurrentWindow();
5277     if (enabled)
5278         window->DC.ItemFlags |= option;
5279     else
5280         window->DC.ItemFlags &= ~option;
5281     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5282 }
5283 
PopItemFlag()5284 void ImGui::PopItemFlag()
5285 {
5286     ImGuiWindow* window = GetCurrentWindow();
5287     window->DC.ItemFlagsStack.pop_back();
5288     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5289 }
5290 
PushAllowKeyboardFocus(bool allow_keyboard_focus)5291 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5292 {
5293     PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus);
5294 }
5295 
PopAllowKeyboardFocus()5296 void ImGui::PopAllowKeyboardFocus()
5297 {
5298     PopItemFlag();
5299 }
5300 
PushButtonRepeat(bool repeat)5301 void ImGui::PushButtonRepeat(bool repeat)
5302 {
5303     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5304 }
5305 
PopButtonRepeat()5306 void ImGui::PopButtonRepeat()
5307 {
5308     PopItemFlag();
5309 }
5310 
PushTextWrapPos(float wrap_pos_x)5311 void ImGui::PushTextWrapPos(float wrap_pos_x)
5312 {
5313     ImGuiWindow* window = GetCurrentWindow();
5314     window->DC.TextWrapPos = wrap_pos_x;
5315     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5316 }
5317 
PopTextWrapPos()5318 void ImGui::PopTextWrapPos()
5319 {
5320     ImGuiWindow* window = GetCurrentWindow();
5321     window->DC.TextWrapPosStack.pop_back();
5322     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5323 }
5324 
5325 // 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
PushStyleColor(ImGuiCol idx,ImU32 col)5326 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5327 {
5328     ImGuiContext& g = *GImGui;
5329     ImGuiColorMod backup;
5330     backup.Col = idx;
5331     backup.BackupValue = g.Style.Colors[idx];
5332     g.ColorModifiers.push_back(backup);
5333     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5334 }
5335 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)5336 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5337 {
5338     ImGuiContext& g = *GImGui;
5339     ImGuiColorMod backup;
5340     backup.Col = idx;
5341     backup.BackupValue = g.Style.Colors[idx];
5342     g.ColorModifiers.push_back(backup);
5343     g.Style.Colors[idx] = col;
5344 }
5345 
PopStyleColor(int count)5346 void ImGui::PopStyleColor(int count)
5347 {
5348     ImGuiContext& g = *GImGui;
5349     while (count > 0)
5350     {
5351         ImGuiColorMod& backup = g.ColorModifiers.back();
5352         g.Style.Colors[backup.Col] = backup.BackupValue;
5353         g.ColorModifiers.pop_back();
5354         count--;
5355     }
5356 }
5357 
5358 struct ImGuiStyleVarInfo
5359 {
5360     ImGuiDataType   Type;
5361     ImU32           Count;
5362     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo5363     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5364 };
5365 
5366 static const ImGuiStyleVarInfo GStyleVarInfo[] =
5367 {
5368     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },              // ImGuiStyleVar_Alpha
5369     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },      // ImGuiStyleVar_WindowPadding
5370     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },     // ImGuiStyleVar_WindowRounding
5371     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },   // ImGuiStyleVar_WindowBorderSize
5372     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },      // ImGuiStyleVar_WindowMinSize
5373     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },   // ImGuiStyleVar_WindowTitleAlign
5374     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },      // ImGuiStyleVar_ChildRounding
5375     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },    // ImGuiStyleVar_ChildBorderSize
5376     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },      // ImGuiStyleVar_PopupRounding
5377     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },    // ImGuiStyleVar_PopupBorderSize
5378     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },       // ImGuiStyleVar_FramePadding
5379     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },      // ImGuiStyleVar_FrameRounding
5380     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },    // ImGuiStyleVar_FrameBorderSize
5381     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },        // ImGuiStyleVar_ItemSpacing
5382     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },   // ImGuiStyleVar_ItemInnerSpacing
5383     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },      // ImGuiStyleVar_IndentSpacing
5384     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },      // ImGuiStyleVar_ScrollbarSize
5385     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },  // ImGuiStyleVar_ScrollbarRounding
5386     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },        // ImGuiStyleVar_GrabMinSize
5387     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },       // ImGuiStyleVar_GrabRounding
5388     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },    // ImGuiStyleVar_ButtonTextAlign
5389 };
5390 
GetStyleVarInfo(ImGuiStyleVar idx)5391 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5392 {
5393     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5394     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5395     return &GStyleVarInfo[idx];
5396 }
5397 
PushStyleVar(ImGuiStyleVar idx,float val)5398 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5399 {
5400     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5401     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5402     {
5403         ImGuiContext& g = *GImGui;
5404         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5405         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5406         *pvar = val;
5407         return;
5408     }
5409     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5410 }
5411 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)5412 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5413 {
5414     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5415     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5416     {
5417         ImGuiContext& g = *GImGui;
5418         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5419         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5420         *pvar = val;
5421         return;
5422     }
5423     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5424 }
5425 
PopStyleVar(int count)5426 void ImGui::PopStyleVar(int count)
5427 {
5428     ImGuiContext& g = *GImGui;
5429     while (count > 0)
5430     {
5431         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5432         ImGuiStyleMod& backup = g.StyleModifiers.back();
5433         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5434         void* data = info->GetVarPtr(&g.Style);
5435         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
5436         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5437         g.StyleModifiers.pop_back();
5438         count--;
5439     }
5440 }
5441 
GetStyleColorName(ImGuiCol idx)5442 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5443 {
5444     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5445     switch (idx)
5446     {
5447     case ImGuiCol_Text: return "Text";
5448     case ImGuiCol_TextDisabled: return "TextDisabled";
5449     case ImGuiCol_WindowBg: return "WindowBg";
5450     case ImGuiCol_ChildBg: return "ChildBg";
5451     case ImGuiCol_PopupBg: return "PopupBg";
5452     case ImGuiCol_Border: return "Border";
5453     case ImGuiCol_BorderShadow: return "BorderShadow";
5454     case ImGuiCol_FrameBg: return "FrameBg";
5455     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5456     case ImGuiCol_FrameBgActive: return "FrameBgActive";
5457     case ImGuiCol_TitleBg: return "TitleBg";
5458     case ImGuiCol_TitleBgActive: return "TitleBgActive";
5459     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5460     case ImGuiCol_MenuBarBg: return "MenuBarBg";
5461     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5462     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5463     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5464     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5465     case ImGuiCol_CheckMark: return "CheckMark";
5466     case ImGuiCol_SliderGrab: return "SliderGrab";
5467     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5468     case ImGuiCol_Button: return "Button";
5469     case ImGuiCol_ButtonHovered: return "ButtonHovered";
5470     case ImGuiCol_ButtonActive: return "ButtonActive";
5471     case ImGuiCol_Header: return "Header";
5472     case ImGuiCol_HeaderHovered: return "HeaderHovered";
5473     case ImGuiCol_HeaderActive: return "HeaderActive";
5474     case ImGuiCol_Separator: return "Separator";
5475     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5476     case ImGuiCol_SeparatorActive: return "SeparatorActive";
5477     case ImGuiCol_ResizeGrip: return "ResizeGrip";
5478     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5479     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5480     case ImGuiCol_PlotLines: return "PlotLines";
5481     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5482     case ImGuiCol_PlotHistogram: return "PlotHistogram";
5483     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5484     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5485     case ImGuiCol_DragDropTarget: return "DragDropTarget";
5486     case ImGuiCol_NavHighlight: return "NavHighlight";
5487     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5488     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5489     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5490     }
5491     IM_ASSERT(0);
5492     return "Unknown";
5493 }
5494 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)5495 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5496 {
5497     if (window->RootWindow == potential_parent)
5498         return true;
5499     while (window != NULL)
5500     {
5501         if (window == potential_parent)
5502             return true;
5503         window = window->ParentWindow;
5504     }
5505     return false;
5506 }
5507 
IsWindowHovered(ImGuiHoveredFlags flags)5508 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5509 {
5510     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
5511     ImGuiContext& g = *GImGui;
5512 
5513     if (flags & ImGuiHoveredFlags_AnyWindow)
5514     {
5515         if (g.HoveredWindow == NULL)
5516             return false;
5517     }
5518     else
5519     {
5520         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5521         {
5522         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5523             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5524                 return false;
5525             break;
5526         case ImGuiHoveredFlags_RootWindow:
5527             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5528                 return false;
5529             break;
5530         case ImGuiHoveredFlags_ChildWindows:
5531             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
5532                 return false;
5533             break;
5534         default:
5535             if (g.HoveredWindow != g.CurrentWindow)
5536                 return false;
5537             break;
5538         }
5539     }
5540 
5541     if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5542         return false;
5543     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5544         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5545             return false;
5546     return true;
5547 }
5548 
IsWindowFocused(ImGuiFocusedFlags flags)5549 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5550 {
5551     ImGuiContext& g = *GImGui;
5552     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
5553 
5554     if (flags & ImGuiFocusedFlags_AnyWindow)
5555         return g.NavWindow != NULL;
5556 
5557     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5558     {
5559     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5560         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5561     case ImGuiFocusedFlags_RootWindow:
5562         return g.NavWindow == g.CurrentWindow->RootWindow;
5563     case ImGuiFocusedFlags_ChildWindows:
5564         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
5565     default:
5566         return g.NavWindow == g.CurrentWindow;
5567     }
5568 }
5569 
5570 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
IsWindowNavFocusable(ImGuiWindow * window)5571 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
5572 {
5573     return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
5574 }
5575 
GetWindowWidth()5576 float ImGui::GetWindowWidth()
5577 {
5578     ImGuiWindow* window = GImGui->CurrentWindow;
5579     return window->Size.x;
5580 }
5581 
GetWindowHeight()5582 float ImGui::GetWindowHeight()
5583 {
5584     ImGuiWindow* window = GImGui->CurrentWindow;
5585     return window->Size.y;
5586 }
5587 
GetWindowPos()5588 ImVec2 ImGui::GetWindowPos()
5589 {
5590     ImGuiContext& g = *GImGui;
5591     ImGuiWindow* window = g.CurrentWindow;
5592     return window->Pos;
5593 }
5594 
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)5595 void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
5596 {
5597     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.
5598     window->Scroll.x = new_scroll_x;
5599     window->DC.CursorMaxPos.x -= window->Scroll.x;
5600 }
5601 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)5602 void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5603 {
5604     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.
5605     window->Scroll.y = new_scroll_y;
5606     window->DC.CursorMaxPos.y -= window->Scroll.y;
5607 }
5608 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)5609 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5610 {
5611     // Test condition (NB: bit 0 is always true) and clear flags for next time
5612     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5613         return;
5614 
5615     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5616     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5617     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5618 
5619     // Set
5620     const ImVec2 old_pos = window->Pos;
5621     window->Pos = ImFloor(pos);
5622     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
5623     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5624 }
5625 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)5626 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5627 {
5628     ImGuiWindow* window = GetCurrentWindowRead();
5629     SetWindowPos(window, pos, cond);
5630 }
5631 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)5632 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5633 {
5634     if (ImGuiWindow* window = FindWindowByName(name))
5635         SetWindowPos(window, pos, cond);
5636 }
5637 
GetWindowSize()5638 ImVec2 ImGui::GetWindowSize()
5639 {
5640     ImGuiWindow* window = GetCurrentWindowRead();
5641     return window->Size;
5642 }
5643 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)5644 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5645 {
5646     // Test condition (NB: bit 0 is always true) and clear flags for next time
5647     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5648         return;
5649 
5650     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5651     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5652 
5653     // Set
5654     if (size.x > 0.0f)
5655     {
5656         window->AutoFitFramesX = 0;
5657         window->SizeFull.x = size.x;
5658     }
5659     else
5660     {
5661         window->AutoFitFramesX = 2;
5662         window->AutoFitOnlyGrows = false;
5663     }
5664     if (size.y > 0.0f)
5665     {
5666         window->AutoFitFramesY = 0;
5667         window->SizeFull.y = size.y;
5668     }
5669     else
5670     {
5671         window->AutoFitFramesY = 2;
5672         window->AutoFitOnlyGrows = false;
5673     }
5674 }
5675 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)5676 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5677 {
5678     SetWindowSize(GImGui->CurrentWindow, size, cond);
5679 }
5680 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)5681 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5682 {
5683     if (ImGuiWindow* window = FindWindowByName(name))
5684         SetWindowSize(window, size, cond);
5685 }
5686 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)5687 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
5688 {
5689     // Test condition (NB: bit 0 is always true) and clear flags for next time
5690     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
5691         return;
5692     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5693 
5694     // Set
5695     window->Collapsed = collapsed;
5696 }
5697 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)5698 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
5699 {
5700     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
5701 }
5702 
IsWindowCollapsed()5703 bool ImGui::IsWindowCollapsed()
5704 {
5705     ImGuiWindow* window = GetCurrentWindowRead();
5706     return window->Collapsed;
5707 }
5708 
IsWindowAppearing()5709 bool ImGui::IsWindowAppearing()
5710 {
5711     ImGuiWindow* window = GetCurrentWindowRead();
5712     return window->Appearing;
5713 }
5714 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)5715 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
5716 {
5717     if (ImGuiWindow* window = FindWindowByName(name))
5718         SetWindowCollapsed(window, collapsed, cond);
5719 }
5720 
SetWindowFocus()5721 void ImGui::SetWindowFocus()
5722 {
5723     FocusWindow(GImGui->CurrentWindow);
5724 }
5725 
SetWindowFocus(const char * name)5726 void ImGui::SetWindowFocus(const char* name)
5727 {
5728     if (name)
5729     {
5730         if (ImGuiWindow* window = FindWindowByName(name))
5731             FocusWindow(window);
5732     }
5733     else
5734     {
5735         FocusWindow(NULL);
5736     }
5737 }
5738 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)5739 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
5740 {
5741     ImGuiContext& g = *GImGui;
5742     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5743     g.NextWindowData.PosVal = pos;
5744     g.NextWindowData.PosPivotVal = pivot;
5745     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
5746 }
5747 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)5748 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
5749 {
5750     ImGuiContext& g = *GImGui;
5751     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5752     g.NextWindowData.SizeVal = size;
5753     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
5754 }
5755 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)5756 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
5757 {
5758     ImGuiContext& g = *GImGui;
5759     g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
5760     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
5761     g.NextWindowData.SizeCallback = custom_callback;
5762     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
5763 }
5764 
SetNextWindowContentSize(const ImVec2 & size)5765 void ImGui::SetNextWindowContentSize(const ImVec2& size)
5766 {
5767     ImGuiContext& g = *GImGui;
5768     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.
5769     g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
5770 }
5771 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)5772 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
5773 {
5774     ImGuiContext& g = *GImGui;
5775     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5776     g.NextWindowData.CollapsedVal = collapsed;
5777     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
5778 }
5779 
SetNextWindowFocus()5780 void ImGui::SetNextWindowFocus()
5781 {
5782     ImGuiContext& g = *GImGui;
5783     g.NextWindowData.FocusCond = ImGuiCond_Always;   // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5784 }
5785 
SetNextWindowBgAlpha(float alpha)5786 void ImGui::SetNextWindowBgAlpha(float alpha)
5787 {
5788     ImGuiContext& g = *GImGui;
5789     g.NextWindowData.BgAlphaVal = alpha;
5790     g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5791 }
5792 
5793 // In window space (not screen space!)
GetContentRegionMax()5794 ImVec2 ImGui::GetContentRegionMax()
5795 {
5796     ImGuiWindow* window = GetCurrentWindowRead();
5797     ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
5798     if (window->DC.ColumnsSet)
5799         mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
5800     return mx;
5801 }
5802 
GetContentRegionAvail()5803 ImVec2 ImGui::GetContentRegionAvail()
5804 {
5805     ImGuiWindow* window = GetCurrentWindowRead();
5806     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5807 }
5808 
GetContentRegionAvailWidth()5809 float ImGui::GetContentRegionAvailWidth()
5810 {
5811     return GetContentRegionAvail().x;
5812 }
5813 
5814 // In window space (not screen space!)
GetWindowContentRegionMin()5815 ImVec2 ImGui::GetWindowContentRegionMin()
5816 {
5817     ImGuiWindow* window = GetCurrentWindowRead();
5818     return window->ContentsRegionRect.Min - window->Pos;
5819 }
5820 
GetWindowContentRegionMax()5821 ImVec2 ImGui::GetWindowContentRegionMax()
5822 {
5823     ImGuiWindow* window = GetCurrentWindowRead();
5824     return window->ContentsRegionRect.Max - window->Pos;
5825 }
5826 
GetWindowContentRegionWidth()5827 float ImGui::GetWindowContentRegionWidth()
5828 {
5829     ImGuiWindow* window = GetCurrentWindowRead();
5830     return window->ContentsRegionRect.GetWidth();
5831 }
5832 
GetTextLineHeight()5833 float ImGui::GetTextLineHeight()
5834 {
5835     ImGuiContext& g = *GImGui;
5836     return g.FontSize;
5837 }
5838 
GetTextLineHeightWithSpacing()5839 float ImGui::GetTextLineHeightWithSpacing()
5840 {
5841     ImGuiContext& g = *GImGui;
5842     return g.FontSize + g.Style.ItemSpacing.y;
5843 }
5844 
GetFrameHeight()5845 float ImGui::GetFrameHeight()
5846 {
5847     ImGuiContext& g = *GImGui;
5848     return g.FontSize + g.Style.FramePadding.y * 2.0f;
5849 }
5850 
GetFrameHeightWithSpacing()5851 float ImGui::GetFrameHeightWithSpacing()
5852 {
5853     ImGuiContext& g = *GImGui;
5854     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5855 }
5856 
GetWindowDrawList()5857 ImDrawList* ImGui::GetWindowDrawList()
5858 {
5859     ImGuiWindow* window = GetCurrentWindow();
5860     return window->DrawList;
5861 }
5862 
GetFont()5863 ImFont* ImGui::GetFont()
5864 {
5865     return GImGui->Font;
5866 }
5867 
GetFontSize()5868 float ImGui::GetFontSize()
5869 {
5870     return GImGui->FontSize;
5871 }
5872 
GetFontTexUvWhitePixel()5873 ImVec2 ImGui::GetFontTexUvWhitePixel()
5874 {
5875     return GImGui->DrawListSharedData.TexUvWhitePixel;
5876 }
5877 
SetWindowFontScale(float scale)5878 void ImGui::SetWindowFontScale(float scale)
5879 {
5880     ImGuiContext& g = *GImGui;
5881     ImGuiWindow* window = GetCurrentWindow();
5882     window->FontWindowScale = scale;
5883     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
5884 }
5885 
5886 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5887 // 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'.
GetCursorPos()5888 ImVec2 ImGui::GetCursorPos()
5889 {
5890     ImGuiWindow* window = GetCurrentWindowRead();
5891     return window->DC.CursorPos - window->Pos + window->Scroll;
5892 }
5893 
GetCursorPosX()5894 float ImGui::GetCursorPosX()
5895 {
5896     ImGuiWindow* window = GetCurrentWindowRead();
5897     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
5898 }
5899 
GetCursorPosY()5900 float ImGui::GetCursorPosY()
5901 {
5902     ImGuiWindow* window = GetCurrentWindowRead();
5903     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
5904 }
5905 
SetCursorPos(const ImVec2 & local_pos)5906 void ImGui::SetCursorPos(const ImVec2& local_pos)
5907 {
5908     ImGuiWindow* window = GetCurrentWindow();
5909     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
5910     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5911 }
5912 
SetCursorPosX(float x)5913 void ImGui::SetCursorPosX(float x)
5914 {
5915     ImGuiWindow* window = GetCurrentWindow();
5916     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
5917     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
5918 }
5919 
SetCursorPosY(float y)5920 void ImGui::SetCursorPosY(float y)
5921 {
5922     ImGuiWindow* window = GetCurrentWindow();
5923     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
5924     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
5925 }
5926 
GetCursorStartPos()5927 ImVec2 ImGui::GetCursorStartPos()
5928 {
5929     ImGuiWindow* window = GetCurrentWindowRead();
5930     return window->DC.CursorStartPos - window->Pos;
5931 }
5932 
GetCursorScreenPos()5933 ImVec2 ImGui::GetCursorScreenPos()
5934 {
5935     ImGuiWindow* window = GetCurrentWindowRead();
5936     return window->DC.CursorPos;
5937 }
5938 
SetCursorScreenPos(const ImVec2 & screen_pos)5939 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
5940 {
5941     ImGuiWindow* window = GetCurrentWindow();
5942     window->DC.CursorPos = screen_pos;
5943     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5944 }
5945 
GetScrollX()5946 float ImGui::GetScrollX()
5947 {
5948     return GImGui->CurrentWindow->Scroll.x;
5949 }
5950 
GetScrollY()5951 float ImGui::GetScrollY()
5952 {
5953     return GImGui->CurrentWindow->Scroll.y;
5954 }
5955 
GetScrollMaxX()5956 float ImGui::GetScrollMaxX()
5957 {
5958     return GetScrollMaxX(GImGui->CurrentWindow);
5959 }
5960 
GetScrollMaxY()5961 float ImGui::GetScrollMaxY()
5962 {
5963     return GetScrollMaxY(GImGui->CurrentWindow);
5964 }
5965 
SetScrollX(float scroll_x)5966 void ImGui::SetScrollX(float scroll_x)
5967 {
5968     ImGuiWindow* window = GetCurrentWindow();
5969     window->ScrollTarget.x = scroll_x;
5970     window->ScrollTargetCenterRatio.x = 0.0f;
5971 }
5972 
SetScrollY(float scroll_y)5973 void ImGui::SetScrollY(float scroll_y)
5974 {
5975     ImGuiWindow* window = GetCurrentWindow();
5976     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
5977     window->ScrollTargetCenterRatio.y = 0.0f;
5978 }
5979 
SetScrollFromPosY(float pos_y,float center_y_ratio)5980 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
5981 {
5982     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
5983     ImGuiWindow* window = GetCurrentWindow();
5984     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
5985     window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
5986     window->ScrollTargetCenterRatio.y = center_y_ratio;
5987 }
5988 
5989 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHere(float center_y_ratio)5990 void ImGui::SetScrollHere(float center_y_ratio)
5991 {
5992     ImGuiWindow* window = GetCurrentWindow();
5993     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
5994     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.
5995     SetScrollFromPosY(target_y, center_y_ratio);
5996 }
5997 
ActivateItem(ImGuiID id)5998 void ImGui::ActivateItem(ImGuiID id)
5999 {
6000     ImGuiContext& g = *GImGui;
6001     g.NavNextActivateId = id;
6002 }
6003 
SetKeyboardFocusHere(int offset)6004 void ImGui::SetKeyboardFocusHere(int offset)
6005 {
6006     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6007     ImGuiWindow* window = GetCurrentWindow();
6008     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6009     window->FocusIdxTabRequestNext = INT_MAX;
6010 }
6011 
SetItemDefaultFocus()6012 void ImGui::SetItemDefaultFocus()
6013 {
6014     ImGuiContext& g = *GImGui;
6015     ImGuiWindow* window = g.CurrentWindow;
6016     if (!window->Appearing)
6017         return;
6018     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6019     {
6020         g.NavInitRequest = false;
6021         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6022         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6023         NavUpdateAnyRequestFlag();
6024         if (!IsItemVisible())
6025             SetScrollHere();
6026     }
6027 }
6028 
SetStateStorage(ImGuiStorage * tree)6029 void ImGui::SetStateStorage(ImGuiStorage* tree)
6030 {
6031     ImGuiWindow* window = GetCurrentWindow();
6032     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6033 }
6034 
GetStateStorage()6035 ImGuiStorage* ImGui::GetStateStorage()
6036 {
6037     ImGuiWindow* window = GetCurrentWindowRead();
6038     return window->DC.StateStorage;
6039 }
6040 
PushID(const char * str_id)6041 void ImGui::PushID(const char* str_id)
6042 {
6043     ImGuiWindow* window = GetCurrentWindowRead();
6044     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6045 }
6046 
PushID(const char * str_id_begin,const char * str_id_end)6047 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6048 {
6049     ImGuiWindow* window = GetCurrentWindowRead();
6050     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6051 }
6052 
PushID(const void * ptr_id)6053 void ImGui::PushID(const void* ptr_id)
6054 {
6055     ImGuiWindow* window = GetCurrentWindowRead();
6056     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6057 }
6058 
PushID(int int_id)6059 void ImGui::PushID(int int_id)
6060 {
6061     const void* ptr_id = (void*)(intptr_t)int_id;
6062     ImGuiWindow* window = GetCurrentWindowRead();
6063     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6064 }
6065 
PopID()6066 void ImGui::PopID()
6067 {
6068     ImGuiWindow* window = GetCurrentWindowRead();
6069     window->IDStack.pop_back();
6070 }
6071 
GetID(const char * str_id)6072 ImGuiID ImGui::GetID(const char* str_id)
6073 {
6074     return GImGui->CurrentWindow->GetID(str_id);
6075 }
6076 
GetID(const char * str_id_begin,const char * str_id_end)6077 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6078 {
6079     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6080 }
6081 
GetID(const void * ptr_id)6082 ImGuiID ImGui::GetID(const void* ptr_id)
6083 {
6084     return GImGui->CurrentWindow->GetID(ptr_id);
6085 }
6086 
IsRectVisible(const ImVec2 & size)6087 bool ImGui::IsRectVisible(const ImVec2& size)
6088 {
6089     ImGuiWindow* window = GetCurrentWindowRead();
6090     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6091 }
6092 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6093 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6094 {
6095     ImGuiWindow* window = GetCurrentWindowRead();
6096     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6097 }
6098 
6099 // 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.)
BeginGroup()6100 void ImGui::BeginGroup()
6101 {
6102     ImGuiContext& g = *GImGui;
6103     ImGuiWindow* window = GetCurrentWindow();
6104 
6105     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6106     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6107     group_data.BackupCursorPos = window->DC.CursorPos;
6108     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6109     group_data.BackupIndent = window->DC.Indent;
6110     group_data.BackupGroupOffset = window->DC.GroupOffset;
6111     group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6112     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6113     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6114     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6115     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6116     group_data.AdvanceCursor = true;
6117 
6118     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6119     window->DC.Indent = window->DC.GroupOffset;
6120     window->DC.CursorMaxPos = window->DC.CursorPos;
6121     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6122     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6123 }
6124 
EndGroup()6125 void ImGui::EndGroup()
6126 {
6127     ImGuiContext& g = *GImGui;
6128     ImGuiWindow* window = GetCurrentWindow();
6129     IM_ASSERT(!window->DC.GroupStack.empty());    // Mismatched BeginGroup()/EndGroup() calls
6130 
6131     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6132 
6133     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6134     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
6135 
6136     window->DC.CursorPos = group_data.BackupCursorPos;
6137     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6138     window->DC.Indent = group_data.BackupIndent;
6139     window->DC.GroupOffset = group_data.BackupGroupOffset;
6140     window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6141     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6142     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6143 
6144     if (group_data.AdvanceCursor)
6145     {
6146         window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, 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.
6147         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
6148         ItemAdd(group_bb, 0);
6149     }
6150 
6151     // 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.
6152     // 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.
6153     // (and if you grep for LastItemId you'll notice it is only used in that context.
6154     if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6155         window->DC.LastItemId = g.ActiveId;
6156     else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6157         window->DC.LastItemId = g.ActiveIdPreviousFrame;
6158     window->DC.LastItemRect = group_bb;
6159 
6160     window->DC.GroupStack.pop_back();
6161 
6162     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
6163 }
6164 
6165 // Gets back to previous line and continue with horizontal layout
6166 //      pos_x == 0      : follow right after previous item
6167 //      pos_x != 0      : align to specified x position (relative to window/group left)
6168 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
6169 //      spacing_w >= 0  : enforce spacing amount
SameLine(float pos_x,float spacing_w)6170 void ImGui::SameLine(float pos_x, float spacing_w)
6171 {
6172     ImGuiWindow* window = GetCurrentWindow();
6173     if (window->SkipItems)
6174         return;
6175 
6176     ImGuiContext& g = *GImGui;
6177     if (pos_x != 0.0f)
6178     {
6179         if (spacing_w < 0.0f) spacing_w = 0.0f;
6180         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6181         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6182     }
6183     else
6184     {
6185         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6186         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6187         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6188     }
6189     window->DC.CurrentLineSize = window->DC.PrevLineSize;
6190     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6191 }
6192 
Indent(float indent_w)6193 void ImGui::Indent(float indent_w)
6194 {
6195     ImGuiContext& g = *GImGui;
6196     ImGuiWindow* window = GetCurrentWindow();
6197     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6198     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6199 }
6200 
Unindent(float indent_w)6201 void ImGui::Unindent(float indent_w)
6202 {
6203     ImGuiContext& g = *GImGui;
6204     ImGuiWindow* window = GetCurrentWindow();
6205     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6206     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6207 }
6208 
6209 //-----------------------------------------------------------------------------
6210 // [SECTION] TOOLTIPS
6211 //-----------------------------------------------------------------------------
6212 
BeginTooltip()6213 void ImGui::BeginTooltip()
6214 {
6215     ImGuiContext& g = *GImGui;
6216     if (g.DragDropWithinSourceOrTarget)
6217     {
6218         // 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)
6219         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6220         // 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.
6221         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6222         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6223         SetNextWindowPos(tooltip_pos);
6224         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6225         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6226         BeginTooltipEx(0, true);
6227     }
6228     else
6229     {
6230         BeginTooltipEx(0, false);
6231     }
6232 }
6233 
6234 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
BeginTooltipEx(ImGuiWindowFlags extra_flags,bool override_previous_tooltip)6235 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6236 {
6237     ImGuiContext& g = *GImGui;
6238     char window_name[16];
6239     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
6240     if (override_previous_tooltip)
6241         if (ImGuiWindow* window = FindWindowByName(window_name))
6242             if (window->Active)
6243             {
6244                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6245                 window->Hidden = true;
6246                 window->HiddenFramesRegular = 1;
6247                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
6248             }
6249     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav;
6250     Begin(window_name, NULL, flags | extra_flags);
6251 }
6252 
EndTooltip()6253 void ImGui::EndTooltip()
6254 {
6255     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
6256     End();
6257 }
6258 
SetTooltipV(const char * fmt,va_list args)6259 void ImGui::SetTooltipV(const char* fmt, va_list args)
6260 {
6261     ImGuiContext& g = *GImGui;
6262     if (g.DragDropWithinSourceOrTarget)
6263         BeginTooltip();
6264     else
6265         BeginTooltipEx(0, true);
6266     TextV(fmt, args);
6267     EndTooltip();
6268 }
6269 
SetTooltip(const char * fmt,...)6270 void ImGui::SetTooltip(const char* fmt, ...)
6271 {
6272     va_list args;
6273     va_start(args, fmt);
6274     SetTooltipV(fmt, args);
6275     va_end(args);
6276 }
6277 
6278 //-----------------------------------------------------------------------------
6279 // [SECTION] POPUPS
6280 //-----------------------------------------------------------------------------
6281 
IsPopupOpen(ImGuiID id)6282 bool ImGui::IsPopupOpen(ImGuiID id)
6283 {
6284     ImGuiContext& g = *GImGui;
6285     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
6286 }
6287 
IsPopupOpen(const char * str_id)6288 bool ImGui::IsPopupOpen(const char* str_id)
6289 {
6290     ImGuiContext& g = *GImGui;
6291     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
6292 }
6293 
GetFrontMostPopupModal()6294 ImGuiWindow* ImGui::GetFrontMostPopupModal()
6295 {
6296     ImGuiContext& g = *GImGui;
6297     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6298         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6299             if (popup->Flags & ImGuiWindowFlags_Modal)
6300                 return popup;
6301     return NULL;
6302 }
6303 
OpenPopup(const char * str_id)6304 void ImGui::OpenPopup(const char* str_id)
6305 {
6306     ImGuiContext& g = *GImGui;
6307     OpenPopupEx(g.CurrentWindow->GetID(str_id));
6308 }
6309 
6310 // Mark popup as open (toggle toward open state).
6311 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6312 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6313 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)6314 void ImGui::OpenPopupEx(ImGuiID id)
6315 {
6316     ImGuiContext& g = *GImGui;
6317     ImGuiWindow* parent_window = g.CurrentWindow;
6318     int current_stack_size = g.CurrentPopupStack.Size;
6319     ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6320     popup_ref.PopupId = id;
6321     popup_ref.Window = NULL;
6322     popup_ref.ParentWindow = parent_window;
6323     popup_ref.OpenFrameCount = g.FrameCount;
6324     popup_ref.OpenParentId = parent_window->IDStack.back();
6325     popup_ref.OpenMousePos = g.IO.MousePos;
6326     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6327 
6328     //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6329     if (g.OpenPopupStack.Size < current_stack_size + 1)
6330     {
6331         g.OpenPopupStack.push_back(popup_ref);
6332     }
6333     else
6334     {
6335         // 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
6336         // 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
6337         // 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.
6338         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6339         {
6340             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6341         }
6342         else
6343         {
6344             // Close child popups if any, then flag popup for open/reopen
6345             g.OpenPopupStack.resize(current_stack_size + 1);
6346             g.OpenPopupStack[current_stack_size] = popup_ref;
6347         }
6348 
6349         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6350         // This is equivalent to what ClosePopupToLevel() does.
6351         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
6352         //    FocusWindow(parent_window);
6353     }
6354 }
6355 
OpenPopupOnItemClick(const char * str_id,int mouse_button)6356 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6357 {
6358     ImGuiWindow* window = GImGui->CurrentWindow;
6359     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6360     {
6361         ImGuiID id = str_id ? window->GetID(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!
6362         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6363         OpenPopupEx(id);
6364         return true;
6365     }
6366     return false;
6367 }
6368 
ClosePopupsOverWindow(ImGuiWindow * ref_window)6369 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6370 {
6371     ImGuiContext& g = *GImGui;
6372     if (g.OpenPopupStack.empty())
6373         return;
6374 
6375     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6376     // Don't close our own child popup windows.
6377     int n = 0;
6378     if (ref_window)
6379     {
6380         for (n = 0; n < g.OpenPopupStack.Size; n++)
6381         {
6382             ImGuiPopupRef& popup = g.OpenPopupStack[n];
6383             if (!popup.Window)
6384                 continue;
6385             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6386             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6387                 continue;
6388 
6389             // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6390             bool has_focus = false;
6391             for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
6392                 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
6393             if (!has_focus)
6394                 break;
6395         }
6396     }
6397     if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
6398         ClosePopupToLevel(n);
6399 }
6400 
ClosePopupToLevel(int remaining)6401 void ImGui::ClosePopupToLevel(int remaining)
6402 {
6403     IM_ASSERT(remaining >= 0);
6404     ImGuiContext& g = *GImGui;
6405     ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6406     if (g.NavLayer == 0)
6407         focus_window = NavRestoreLastChildNavWindow(focus_window);
6408     FocusWindow(focus_window);
6409     focus_window->DC.NavHideHighlightOneFrame = true;
6410     g.OpenPopupStack.resize(remaining);
6411 }
6412 
ClosePopup(ImGuiID id)6413 void ImGui::ClosePopup(ImGuiID id)
6414 {
6415     if (!IsPopupOpen(id))
6416         return;
6417     ImGuiContext& g = *GImGui;
6418     ClosePopupToLevel(g.OpenPopupStack.Size - 1);
6419 }
6420 
6421 // Close the popup we have begin-ed into.
CloseCurrentPopup()6422 void ImGui::CloseCurrentPopup()
6423 {
6424     ImGuiContext& g = *GImGui;
6425     int popup_idx = g.CurrentPopupStack.Size - 1;
6426     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6427         return;
6428     while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
6429         popup_idx--;
6430     ClosePopupToLevel(popup_idx);
6431 }
6432 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)6433 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6434 {
6435     ImGuiContext& g = *GImGui;
6436     if (!IsPopupOpen(id))
6437     {
6438         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6439         return false;
6440     }
6441 
6442     char name[20];
6443     if (extra_flags & ImGuiWindowFlags_ChildMenu)
6444         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size);    // Recycle windows based on depth
6445     else
6446         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6447 
6448     bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
6449     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6450         EndPopup();
6451 
6452     return is_open;
6453 }
6454 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)6455 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6456 {
6457     ImGuiContext& g = *GImGui;
6458     if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
6459     {
6460         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6461         return false;
6462     }
6463     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6464 }
6465 
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)6466 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6467 {
6468     ImGuiContext& g = *GImGui;
6469     ImGuiWindow* window = g.CurrentWindow;
6470     const ImGuiID id = window->GetID(name);
6471     if (!IsPopupOpen(id))
6472     {
6473         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6474         return false;
6475     }
6476 
6477     // Center modal windows by default
6478     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6479     if (g.NextWindowData.PosCond == 0)
6480         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
6481 
6482     bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
6483     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6484     {
6485         EndPopup();
6486         if (is_open)
6487             ClosePopup(id);
6488         return false;
6489     }
6490     return is_open;
6491 }
6492 
EndPopup()6493 void ImGui::EndPopup()
6494 {
6495     ImGuiContext& g = *GImGui; (void)g;
6496     IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
6497     IM_ASSERT(g.CurrentPopupStack.Size > 0);
6498 
6499     // Make all menus and popups wrap around for now, may need to expose that policy.
6500     NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
6501 
6502     End();
6503 }
6504 
6505 // This is a helper to handle the simplest case of associating one named popup to one given widget.
6506 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6507 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)6508 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6509 {
6510     ImGuiWindow* window = GImGui->CurrentWindow;
6511     ImGuiID id = str_id ? window->GetID(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!
6512     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6513     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6514         OpenPopupEx(id);
6515     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6516 }
6517 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)6518 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6519 {
6520     if (!str_id)
6521         str_id = "window_context";
6522     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6523     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6524         if (also_over_items || !IsAnyItemHovered())
6525             OpenPopupEx(id);
6526     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6527 }
6528 
BeginPopupContextVoid(const char * str_id,int mouse_button)6529 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
6530 {
6531     if (!str_id)
6532         str_id = "void_context";
6533     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6534     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
6535         OpenPopupEx(id);
6536     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6537 }
6538 
GetWindowAllowedExtentRect(ImGuiWindow *)6539 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
6540 {
6541     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
6542     ImRect r_screen = GetViewportRect();
6543     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
6544     return r_screen;
6545 }
6546 
6547 // 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.)
6548 // 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.
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy)6549 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
6550 {
6551     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
6552     //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
6553     //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
6554 
6555     // Combo Box policy (we want a connecting edge)
6556     if (policy == ImGuiPopupPositionPolicy_ComboBox)
6557     {
6558         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
6559         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6560         {
6561             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6562             if (n != -1 && dir == *last_dir) // Already tried this direction?
6563                 continue;
6564             ImVec2 pos;
6565             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
6566             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
6567             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
6568             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
6569             if (!r_outer.Contains(ImRect(pos, pos + size)))
6570                 continue;
6571             *last_dir = dir;
6572             return pos;
6573         }
6574     }
6575 
6576     // Default popup policy
6577     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
6578     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6579     {
6580         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6581         if (n != -1 && dir == *last_dir) // Already tried this direction?
6582             continue;
6583         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);
6584         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);
6585         if (avail_w < size.x || avail_h < size.y)
6586             continue;
6587         ImVec2 pos;
6588         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
6589         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
6590         *last_dir = dir;
6591         return pos;
6592     }
6593 
6594     // Fallback, try to keep within display
6595     *last_dir = ImGuiDir_None;
6596     ImVec2 pos = ref_pos;
6597     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
6598     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
6599     return pos;
6600 }
6601 
FindBestWindowPosForPopup(ImGuiWindow * window)6602 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
6603 {
6604     ImGuiContext& g = *GImGui;
6605 
6606     ImRect r_outer = GetWindowAllowedExtentRect(window);
6607     if (window->Flags & ImGuiWindowFlags_ChildMenu)
6608     {
6609         // 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.
6610         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
6611         IM_ASSERT(g.CurrentWindow == window);
6612         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
6613         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).
6614         ImRect r_avoid;
6615         if (parent_window->DC.MenuBarAppending)
6616             r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
6617         else
6618             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);
6619         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6620     }
6621     if (window->Flags & ImGuiWindowFlags_Popup)
6622     {
6623         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
6624         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6625     }
6626     if (window->Flags & ImGuiWindowFlags_Tooltip)
6627     {
6628         // Position tooltip (always follows mouse)
6629         float sc = g.Style.MouseCursorScale;
6630         ImVec2 ref_pos = NavCalcPreferredRefPos();
6631         ImRect r_avoid;
6632         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
6633             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
6634         else
6635             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.
6636         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6637         if (window->AutoPosLastDirection == ImGuiDir_None)
6638             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.
6639         return pos;
6640     }
6641     IM_ASSERT(0);
6642     return window->Pos;
6643 }
6644 
6645 //-----------------------------------------------------------------------------
6646 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
6647 //-----------------------------------------------------------------------------
6648 
ImGetDirQuadrantFromDelta(float dx,float dy)6649 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
6650 {
6651     if (ImFabs(dx) > ImFabs(dy))
6652         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
6653     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
6654 }
6655 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)6656 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
6657 {
6658     if (a1 < b0)
6659         return a1 - b0;
6660     if (b1 < a0)
6661         return a0 - b1;
6662     return 0.0f;
6663 }
6664 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)6665 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
6666 {
6667     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
6668     {
6669         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
6670         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
6671     }
6672     else
6673     {
6674         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
6675         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
6676     }
6677 }
6678 
6679 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)6680 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
6681 {
6682     ImGuiContext& g = *GImGui;
6683     ImGuiWindow* window = g.CurrentWindow;
6684     if (g.NavLayer != window->DC.NavLayerCurrent)
6685         return false;
6686 
6687     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)
6688     g.NavScoringCount++;
6689 
6690     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
6691     if (window->ParentWindow == g.NavWindow)
6692     {
6693         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
6694         if (!window->ClipRect.Contains(cand))
6695             return false;
6696         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
6697     }
6698 
6699     // 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)
6700     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
6701     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
6702 
6703     // Compute distance between boxes
6704     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
6705     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
6706     float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
6707     if (dby != 0.0f && dbx != 0.0f)
6708        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
6709     float dist_box = ImFabs(dbx) + ImFabs(dby);
6710 
6711     // 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)
6712     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
6713     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
6714     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
6715 
6716     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
6717     ImGuiDir quadrant;
6718     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
6719     if (dbx != 0.0f || dby != 0.0f)
6720     {
6721         // For non-overlapping boxes, use distance between boxes
6722         dax = dbx;
6723         day = dby;
6724         dist_axial = dist_box;
6725         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
6726     }
6727     else if (dcx != 0.0f || dcy != 0.0f)
6728     {
6729         // For overlapping boxes with different centers, use distance between centers
6730         dax = dcx;
6731         day = dcy;
6732         dist_axial = dist_center;
6733         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
6734     }
6735     else
6736     {
6737         // 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)
6738         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
6739     }
6740 
6741 #if IMGUI_DEBUG_NAV_SCORING
6742     char buf[128];
6743     if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
6744     {
6745         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]);
6746         ImDrawList* draw_list = ImGui::GetOverlayDrawList();
6747         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
6748         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
6749         draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
6750         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
6751     }
6752     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
6753     {
6754         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
6755         if (quadrant == g.NavMoveDir)
6756         {
6757             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
6758             ImDrawList* draw_list = ImGui::GetOverlayDrawList();
6759             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
6760             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
6761         }
6762     }
6763  #endif
6764 
6765     // Is it in the quadrant we're interesting in moving to?
6766     bool new_best = false;
6767     if (quadrant == g.NavMoveDir)
6768     {
6769         // Does it beat the current best candidate?
6770         if (dist_box < result->DistBox)
6771         {
6772             result->DistBox = dist_box;
6773             result->DistCenter = dist_center;
6774             return true;
6775         }
6776         if (dist_box == result->DistBox)
6777         {
6778             // Try using distance between center points to break ties
6779             if (dist_center < result->DistCenter)
6780             {
6781                 result->DistCenter = dist_center;
6782                 new_best = true;
6783             }
6784             else if (dist_center == result->DistCenter)
6785             {
6786                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
6787                 // (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),
6788                 // 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.
6789                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
6790                     new_best = true;
6791             }
6792         }
6793     }
6794 
6795     // 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
6796     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
6797     // 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.
6798     // 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.
6799     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
6800     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
6801         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
6802             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))
6803             {
6804                 result->DistAxial = dist_axial;
6805                 new_best = true;
6806             }
6807 
6808     return new_best;
6809 }
6810 
6811 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
NavProcessItem(ImGuiWindow * window,const ImRect & nav_bb,const ImGuiID id)6812 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
6813 {
6814     ImGuiContext& g = *GImGui;
6815     //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.
6816     //    return;
6817 
6818     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
6819     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
6820 
6821     // Process Init Request
6822     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
6823     {
6824         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
6825         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
6826         {
6827             g.NavInitResultId = id;
6828             g.NavInitResultRectRel = nav_bb_rel;
6829         }
6830         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
6831         {
6832             g.NavInitRequest = false; // Found a match, clear request
6833             NavUpdateAnyRequestFlag();
6834         }
6835     }
6836 
6837     // Process Move Request (scoring for navigation)
6838     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
6839     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
6840     {
6841         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
6842 #if IMGUI_DEBUG_NAV_SCORING
6843         // [DEBUG] Score all items in NavWindow at all times
6844         if (!g.NavMoveRequest)
6845             g.NavMoveDir = g.NavMoveDirLast;
6846         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
6847 #else
6848         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
6849 #endif
6850         if (new_best)
6851         {
6852             result->ID = id;
6853             result->Window = window;
6854             result->RectRel = nav_bb_rel;
6855         }
6856 
6857         const float VISIBLE_RATIO = 0.70f;
6858         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
6859             if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
6860                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
6861                 {
6862                     result = &g.NavMoveResultLocalVisibleSet;
6863                     result->ID = id;
6864                     result->Window = window;
6865                     result->RectRel = nav_bb_rel;
6866                 }
6867     }
6868 
6869     // Update window-relative bounding box of navigated item
6870     if (g.NavId == id)
6871     {
6872         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
6873         g.NavLayer = window->DC.NavLayerCurrent;
6874         g.NavIdIsAlive = true;
6875         g.NavIdTabCounter = window->FocusIdxTabCounter;
6876         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
6877     }
6878 }
6879 
NavMoveRequestButNoResultYet()6880 bool ImGui::NavMoveRequestButNoResultYet()
6881 {
6882     ImGuiContext& g = *GImGui;
6883     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
6884 }
6885 
NavMoveRequestCancel()6886 void ImGui::NavMoveRequestCancel()
6887 {
6888     ImGuiContext& g = *GImGui;
6889     g.NavMoveRequest = false;
6890     NavUpdateAnyRequestFlag();
6891 }
6892 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)6893 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
6894 {
6895     ImGuiContext& g = *GImGui;
6896     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
6897     ImGui::NavMoveRequestCancel();
6898     g.NavMoveDir = move_dir;
6899     g.NavMoveClipDir = clip_dir;
6900     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
6901     g.NavMoveRequestFlags = move_flags;
6902     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
6903 }
6904 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)6905 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
6906 {
6907     ImGuiContext& g = *GImGui;
6908     if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
6909         return;
6910     IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
6911     ImRect bb_rel = window->NavRectRel[0];
6912 
6913     ImGuiDir clip_dir = g.NavMoveDir;
6914     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
6915     {
6916         bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
6917         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
6918         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6919     }
6920     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
6921     {
6922         bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
6923         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
6924         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6925     }
6926     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
6927     {
6928         bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
6929         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
6930         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6931     }
6932     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
6933     {
6934         bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
6935         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
6936         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6937     }
6938 }
6939 
NavSaveLastChildNavWindow(ImGuiWindow * nav_window)6940 static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
6941 {
6942     ImGuiWindow* parent_window = nav_window;
6943     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
6944         parent_window = parent_window->ParentWindow;
6945     if (parent_window && parent_window != nav_window)
6946         parent_window->NavLastChildNavWindow = nav_window;
6947 }
6948 
6949 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)6950 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
6951 {
6952     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
6953 }
6954 
NavRestoreLayer(int layer)6955 static void NavRestoreLayer(int layer)
6956 {
6957     ImGuiContext& g = *GImGui;
6958     g.NavLayer = layer;
6959     if (layer == 0)
6960         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
6961     if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
6962         ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
6963     else
6964         ImGui::NavInitWindow(g.NavWindow, true);
6965 }
6966 
NavUpdateAnyRequestFlag()6967 static inline void ImGui::NavUpdateAnyRequestFlag()
6968 {
6969     ImGuiContext& g = *GImGui;
6970     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
6971     if (g.NavAnyRequest)
6972         IM_ASSERT(g.NavWindow != NULL);
6973 }
6974 
6975 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)6976 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
6977 {
6978     ImGuiContext& g = *GImGui;
6979     IM_ASSERT(window == g.NavWindow);
6980     bool init_for_nav = false;
6981     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
6982         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
6983             init_for_nav = true;
6984     if (init_for_nav)
6985     {
6986         SetNavID(0, g.NavLayer);
6987         g.NavInitRequest = true;
6988         g.NavInitRequestFromMove = false;
6989         g.NavInitResultId = 0;
6990         g.NavInitResultRectRel = ImRect();
6991         NavUpdateAnyRequestFlag();
6992     }
6993     else
6994     {
6995         g.NavId = window->NavLastIds[0];
6996     }
6997 }
6998 
NavCalcPreferredRefPos()6999 static ImVec2 ImGui::NavCalcPreferredRefPos()
7000 {
7001     ImGuiContext& g = *GImGui;
7002     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7003         return ImFloor(g.IO.MousePos);
7004 
7005     // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item
7006     const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7007     ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
7008     ImRect visible_rect = GetViewportRect();
7009     return ImFloor(ImClamp(pos, visible_rect.Min, 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.
7010 }
7011 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)7012 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7013 {
7014     ImGuiContext& g = *GImGui;
7015     if (mode == ImGuiInputReadMode_Down)
7016         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
7017 
7018     const float t = g.IO.NavInputsDownDuration[n];
7019     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
7020         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7021     if (t < 0.0f)
7022         return 0.0f;
7023     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
7024         return (t == 0.0f) ? 1.0f : 0.0f;
7025     if (mode == ImGuiInputReadMode_Repeat)
7026         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
7027     if (mode == ImGuiInputReadMode_RepeatSlow)
7028         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
7029     if (mode == ImGuiInputReadMode_RepeatFast)
7030         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
7031     return 0.0f;
7032 }
7033 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)7034 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7035 {
7036     ImVec2 delta(0.0f, 0.0f);
7037     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7038         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
7039     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7040         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
7041     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7042         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
7043     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
7044         delta *= slow_factor;
7045     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
7046         delta *= fast_factor;
7047     return delta;
7048 }
7049 
7050 // Scroll to keep newly navigated item fully into view
7051 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,const ImRect & item_rect)7052 static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7053 {
7054     ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7055     //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7056     if (window_rect.Contains(item_rect))
7057         return;
7058 
7059     ImGuiContext& g = *GImGui;
7060     if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7061     {
7062         window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7063         window->ScrollTargetCenterRatio.x = 0.0f;
7064     }
7065     else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7066     {
7067         window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7068         window->ScrollTargetCenterRatio.x = 1.0f;
7069     }
7070     if (item_rect.Min.y < window_rect.Min.y)
7071     {
7072         window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7073         window->ScrollTargetCenterRatio.y = 0.0f;
7074     }
7075     else if (item_rect.Max.y >= window_rect.Max.y)
7076     {
7077         window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7078         window->ScrollTargetCenterRatio.y = 1.0f;
7079     }
7080 }
7081 
NavUpdate()7082 static void ImGui::NavUpdate()
7083 {
7084     ImGuiContext& g = *GImGui;
7085     g.IO.WantSetMousePos = false;
7086 #if 0
7087     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);
7088 #endif
7089 
7090     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7091     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7092     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7093     if (nav_gamepad_active)
7094         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)
7095             g.NavInputSource = ImGuiInputSource_NavGamepad;
7096 
7097     // Update Keyboard->Nav inputs mapping
7098     if (nav_keyboard_active)
7099     {
7100         #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7101         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
7102         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
7103         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
7104         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7105         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7106         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
7107         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7108         if (g.IO.KeyCtrl)   g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7109         if (g.IO.KeyShift)  g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7110         if (g.IO.KeyAlt)    g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
7111         #undef NAV_MAP_KEY
7112     }
7113     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
7114     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7115         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;
7116 
7117     // Process navigation init request (select first/default focus)
7118     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7119     {
7120         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7121         IM_ASSERT(g.NavWindow);
7122         if (g.NavInitRequestFromMove)
7123             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
7124         else
7125             SetNavID(g.NavInitResultId, g.NavLayer);
7126         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7127     }
7128     g.NavInitRequest = false;
7129     g.NavInitRequestFromMove = false;
7130     g.NavInitResultId = 0;
7131     g.NavJustMovedToId = 0;
7132 
7133     // Process navigation move request
7134     if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0))
7135         NavUpdateMoveResult();
7136 
7137     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7138     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7139     {
7140         IM_ASSERT(g.NavMoveRequest);
7141         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7142             g.NavDisableHighlight = false;
7143         g.NavMoveRequestForward = ImGuiNavForward_None;
7144     }
7145 
7146     // Apply application mouse position movement, after we had a chance to process move request result.
7147     if (g.NavMousePosDirty && g.NavIdIsAlive)
7148     {
7149         // Set mouse position given our knowledge of the navigated item position from last frame
7150         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7151         {
7152             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7153             {
7154                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7155                 g.IO.WantSetMousePos = true;
7156             }
7157         }
7158         g.NavMousePosDirty = false;
7159     }
7160     g.NavIdIsAlive = false;
7161     g.NavJustTabbedId = 0;
7162     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7163 
7164     // 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
7165     if (g.NavWindow)
7166         NavSaveLastChildNavWindow(g.NavWindow);
7167     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7168         g.NavWindow->NavLastChildNavWindow = NULL;
7169 
7170     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7171     NavUpdateWindowing();
7172 
7173     // Set output flags for user application
7174     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7175     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
7176 
7177     // Process NavCancel input (to close a popup, get back to parent, clear focus)
7178     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
7179     {
7180         if (g.ActiveId != 0)
7181         {
7182             ClearActiveID();
7183         }
7184         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7185         {
7186             // Exit child window
7187             ImGuiWindow* child_window = g.NavWindow;
7188             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7189             IM_ASSERT(child_window->ChildId != 0);
7190             FocusWindow(parent_window);
7191             SetNavID(child_window->ChildId, 0);
7192             g.NavIdIsAlive = false;
7193             if (g.NavDisableMouseHover)
7194                 g.NavMousePosDirty = true;
7195         }
7196         else if (g.OpenPopupStack.Size > 0)
7197         {
7198             // Close open popup/menu
7199             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7200                 ClosePopupToLevel(g.OpenPopupStack.Size - 1);
7201         }
7202         else if (g.NavLayer != 0)
7203         {
7204             // Leave the "menu" layer
7205             NavRestoreLayer(0);
7206         }
7207         else
7208         {
7209             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7210             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7211                 g.NavWindow->NavLastIds[0] = 0;
7212             g.NavId = 0;
7213         }
7214     }
7215 
7216     // Process manual activation request
7217     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7218     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7219     {
7220         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
7221         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
7222         if (g.ActiveId == 0 && activate_pressed)
7223             g.NavActivateId = g.NavId;
7224         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7225             g.NavActivateDownId = g.NavId;
7226         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7227             g.NavActivatePressedId = g.NavId;
7228         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
7229             g.NavInputId = g.NavId;
7230     }
7231     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7232         g.NavDisableHighlight = true;
7233     if (g.NavActivateId != 0)
7234         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7235     g.NavMoveRequest = false;
7236 
7237     // Process programmatic activation request
7238     if (g.NavNextActivateId != 0)
7239         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7240     g.NavNextActivateId = 0;
7241 
7242     // Initiate directional inputs request
7243     const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7244     if (g.NavMoveRequestForward == ImGuiNavForward_None)
7245     {
7246         g.NavMoveDir = ImGuiDir_None;
7247         g.NavMoveRequestFlags = 0;
7248         if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7249         {
7250             if ((allowed_dir_flags & (1<<ImGuiDir_Left))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7251             if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7252             if ((allowed_dir_flags & (1<<ImGuiDir_Up))    && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp,   ImGuiNavInput_KeyUp_,   ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7253             if ((allowed_dir_flags & (1<<ImGuiDir_Down))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7254         }
7255         g.NavMoveClipDir = g.NavMoveDir;
7256     }
7257     else
7258     {
7259         // 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)
7260         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7261         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7262         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7263         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7264     }
7265 
7266     // Update PageUp/PageDown scroll
7267     float nav_scoring_rect_offset_y = 0.0f;
7268     if (nav_keyboard_active)
7269         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7270 
7271     // 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
7272     if (g.NavMoveDir != ImGuiDir_None)
7273     {
7274         g.NavMoveRequest = true;
7275         g.NavMoveDirLast = g.NavMoveDir;
7276     }
7277     if (g.NavMoveRequest && g.NavId == 0)
7278     {
7279         g.NavInitRequest = g.NavInitRequestFromMove = true;
7280         g.NavInitResultId = 0;
7281         g.NavDisableHighlight = false;
7282     }
7283     NavUpdateAnyRequestFlag();
7284 
7285     // Scrolling
7286     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7287     {
7288         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7289         ImGuiWindow* window = g.NavWindow;
7290         const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
7291         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7292         {
7293             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7294                 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7295             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7296                 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7297         }
7298 
7299         // *Normal* Manual scroll with NavScrollXXX keys
7300         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7301         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
7302         if (scroll_dir.x != 0.0f && window->ScrollbarX)
7303         {
7304             SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
7305             g.NavMoveFromClampedRefRect = true;
7306         }
7307         if (scroll_dir.y != 0.0f)
7308         {
7309             SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
7310             g.NavMoveFromClampedRefRect = true;
7311         }
7312     }
7313 
7314     // Reset search results
7315     g.NavMoveResultLocal.Clear();
7316     g.NavMoveResultLocalVisibleSet.Clear();
7317     g.NavMoveResultOther.Clear();
7318 
7319     // 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
7320     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7321     {
7322         ImGuiWindow* window = g.NavWindow;
7323         ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7324         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
7325         {
7326             float pad = window->CalcFontSize() * 0.5f;
7327             window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item
7328             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
7329             g.NavId = 0;
7330         }
7331         g.NavMoveFromClampedRefRect = false;
7332     }
7333 
7334     // 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)
7335     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7336     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7337     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
7338     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
7339     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7340     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().
7341     //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7342     g.NavScoringCount = 0;
7343 #if IMGUI_DEBUG_NAV_RECTS
7344     if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList()->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
7345     if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
7346 #endif
7347 }
7348 
NavUpdateMoveResult()7349 static void ImGui::NavUpdateMoveResult()
7350 {
7351     // Select which result to use
7352     ImGuiContext& g = *GImGui;
7353     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7354 
7355     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7356     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7357         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7358             result = &g.NavMoveResultLocalVisibleSet;
7359 
7360     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7361     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7362         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7363             result = &g.NavMoveResultOther;
7364     IM_ASSERT(g.NavWindow && result->Window);
7365 
7366     // Scroll to keep newly navigated item fully into view.
7367     if (g.NavLayer == 0)
7368     {
7369         ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7370         NavScrollToBringItemIntoView(result->Window, rect_abs);
7371 
7372         // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7373         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false);
7374         ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7375         result->RectRel.Translate(delta_scroll);
7376 
7377         // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7378         if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7379             NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7380     }
7381 
7382     // Apply result from previous frame navigation directional move request
7383     ClearActiveID();
7384     g.NavWindow = result->Window;
7385     SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
7386     g.NavJustMovedToId = result->ID;
7387     g.NavMoveFromClampedRefRect = false;
7388 }
7389 
NavUpdatePageUpPageDown(int allowed_dir_flags)7390 static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7391 {
7392     ImGuiContext& g = *GImGui;
7393     if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7394     {
7395         ImGuiWindow* window = g.NavWindow;
7396         bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7397         bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7398         if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held))
7399         {
7400             if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7401             {
7402                 // Fallback manual-scroll when window has no navigable item
7403                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7404                     SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
7405                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7406                     SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
7407             }
7408             else
7409             {
7410                 const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7411                 const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7412                 float nav_scoring_rect_offset_y = 0.0f;
7413                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7414                 {
7415                     nav_scoring_rect_offset_y = -page_offset_y;
7416                     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)
7417                     g.NavMoveClipDir = ImGuiDir_Up;
7418                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7419                 }
7420                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7421                 {
7422                     nav_scoring_rect_offset_y = +page_offset_y;
7423                     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)
7424                     g.NavMoveClipDir = ImGuiDir_Down;
7425                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7426                 }
7427                 return nav_scoring_rect_offset_y;
7428             }
7429         }
7430     }
7431     return 0.0f;
7432 }
7433 
FindWindowIndex(ImGuiWindow * window)7434 static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7435 {
7436     ImGuiContext& g = *GImGui;
7437     for (int i = g.Windows.Size-1; i >= 0; i--)
7438         if (g.Windows[i] == window)
7439             return i;
7440     return -1;
7441 }
7442 
FindWindowNavFocusable(int i_start,int i_stop,int dir)7443 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7444 {
7445     ImGuiContext& g = *GImGui;
7446     for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir)
7447         if (ImGui::IsWindowNavFocusable(g.Windows[i]))
7448             return g.Windows[i];
7449     return NULL;
7450 }
7451 
NavUpdateWindowingHighlightWindow(int focus_change_dir)7452 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7453 {
7454     ImGuiContext& g = *GImGui;
7455     IM_ASSERT(g.NavWindowingTarget);
7456     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7457         return;
7458 
7459     const int i_current = FindWindowIndex(g.NavWindowingTarget);
7460     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
7461     if (!window_target)
7462         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir);
7463     if (window_target) // Don't reset windowing target if there's a single window in the list
7464         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7465     g.NavWindowingToggleLayer = false;
7466 }
7467 
7468 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()7469 static void ImGui::NavUpdateWindowing()
7470 {
7471     ImGuiContext& g = *GImGui;
7472     ImGuiWindow* apply_focus_window = NULL;
7473     bool apply_toggle_layer = false;
7474 
7475     ImGuiWindow* modal_window = GetFrontMostPopupModal();
7476     if (modal_window != NULL)
7477     {
7478         g.NavWindowingTarget = NULL;
7479         return;
7480     }
7481 
7482     // Fade out
7483     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7484     {
7485         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
7486         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7487             g.NavWindowingTargetAnim = NULL;
7488     }
7489 
7490     // Start CTRL-TAB or Square+L/R window selection
7491     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
7492     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7493     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7494         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.Windows.Size - 1, -INT_MAX, -1))
7495         {
7496             g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
7497             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
7498             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
7499             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
7500         }
7501 
7502     // Gamepad update
7503     g.NavWindowingTimer += g.IO.DeltaTime;
7504     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
7505     {
7506         // 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
7507         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
7508 
7509         // Select window to focus
7510         const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
7511         if (focus_change_dir != 0)
7512         {
7513             NavUpdateWindowingHighlightWindow(focus_change_dir);
7514             g.NavWindowingHighlightAlpha = 1.0f;
7515         }
7516 
7517         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
7518         if (!IsNavInputDown(ImGuiNavInput_Menu))
7519         {
7520             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
7521             if (g.NavWindowingToggleLayer && g.NavWindow)
7522                 apply_toggle_layer = true;
7523             else if (!g.NavWindowingToggleLayer)
7524                 apply_focus_window = g.NavWindowingTarget;
7525             g.NavWindowingTarget = NULL;
7526         }
7527     }
7528 
7529     // Keyboard: Focus
7530     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
7531     {
7532         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
7533         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
7534         if (IsKeyPressedMap(ImGuiKey_Tab, true))
7535             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
7536         if (!g.IO.KeyCtrl)
7537             apply_focus_window = g.NavWindowingTarget;
7538     }
7539 
7540     // Keyboard: Press and Release ALT to toggle menu layer
7541     // 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
7542     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
7543         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
7544             apply_toggle_layer = true;
7545 
7546     // Move window
7547     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
7548     {
7549         ImVec2 move_delta;
7550         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
7551             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
7552         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
7553             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
7554         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
7555         {
7556             const float NAV_MOVE_SPEED = 800.0f;
7557             const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
7558             g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
7559             g.NavDisableMouseHover = true;
7560             MarkIniSettingsDirty(g.NavWindowingTarget);
7561         }
7562     }
7563 
7564     // Apply final focus
7565     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
7566     {
7567         g.NavDisableHighlight = false;
7568         g.NavDisableMouseHover = true;
7569         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
7570         ClosePopupsOverWindow(apply_focus_window);
7571         FocusWindow(apply_focus_window);
7572         if (apply_focus_window->NavLastIds[0] == 0)
7573             NavInitWindow(apply_focus_window, false);
7574 
7575         // If the window only has a menu layer, select it directly
7576         if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1))
7577             g.NavLayer = 1;
7578     }
7579     if (apply_focus_window)
7580         g.NavWindowingTarget = NULL;
7581 
7582     // Apply menu/layer toggle
7583     if (apply_toggle_layer && g.NavWindow)
7584     {
7585         // Move to parent menu if necessary
7586         ImGuiWindow* new_nav_window = g.NavWindow;
7587         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)
7588             new_nav_window = new_nav_window->ParentWindow;
7589         if (new_nav_window != g.NavWindow)
7590         {
7591             ImGuiWindow* old_nav_window = g.NavWindow;
7592             FocusWindow(new_nav_window);
7593             new_nav_window->NavLastChildNavWindow = old_nav_window;
7594         }
7595         g.NavDisableHighlight = false;
7596         g.NavDisableMouseHover = true;
7597         NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0);
7598     }
7599 }
7600 
7601 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)7602 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
7603 {
7604     if (window->Flags & ImGuiWindowFlags_Popup)
7605         return "(Popup)";
7606     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
7607         return "(Main menu bar)";
7608     return "(Untitled)";
7609 }
7610 
7611 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingList()7612 void ImGui::NavUpdateWindowingList()
7613 {
7614     ImGuiContext& g = *GImGui;
7615     IM_ASSERT(g.NavWindowingTarget != NULL);
7616 
7617     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
7618         return;
7619 
7620     if (g.NavWindowingList == NULL)
7621         g.NavWindowingList = FindWindowByName("###NavWindowingList");
7622     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
7623     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
7624     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
7625     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
7626     for (int n = g.Windows.Size - 1; n >= 0; n--)
7627     {
7628         ImGuiWindow* window = g.Windows[n];
7629         if (!IsWindowNavFocusable(window))
7630             continue;
7631         const char* label = window->Name;
7632         if (label == FindRenderedTextEnd(label))
7633             label = GetFallbackWindowNameForWindowingList(window);
7634         Selectable(label, g.NavWindowingTarget == window);
7635     }
7636     End();
7637     PopStyleVar();
7638 }
7639 
7640 //-----------------------------------------------------------------------------
7641 // [SECTION] COLUMNS
7642 // In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
7643 //-----------------------------------------------------------------------------
7644 
NextColumn()7645 void ImGui::NextColumn()
7646 {
7647     ImGuiWindow* window = GetCurrentWindow();
7648     if (window->SkipItems || window->DC.ColumnsSet == NULL)
7649         return;
7650 
7651     ImGuiContext& g = *GImGui;
7652     PopItemWidth();
7653     PopClipRect();
7654 
7655     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7656     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
7657     if (++columns->Current < columns->Count)
7658     {
7659         // Columns 1+ cancel out IndentX
7660         window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
7661         window->DrawList->ChannelsSetCurrent(columns->Current);
7662     }
7663     else
7664     {
7665         window->DC.ColumnsOffset.x = 0.0f;
7666         window->DrawList->ChannelsSetCurrent(0);
7667         columns->Current = 0;
7668         columns->LineMinY = columns->LineMaxY;
7669     }
7670     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7671     window->DC.CursorPos.y = columns->LineMinY;
7672     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
7673     window->DC.CurrentLineTextBaseOffset = 0.0f;
7674 
7675     PushColumnClipRect();
7676     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
7677 }
7678 
GetColumnIndex()7679 int ImGui::GetColumnIndex()
7680 {
7681     ImGuiWindow* window = GetCurrentWindowRead();
7682     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
7683 }
7684 
GetColumnsCount()7685 int ImGui::GetColumnsCount()
7686 {
7687     ImGuiWindow* window = GetCurrentWindowRead();
7688     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
7689 }
7690 
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)7691 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
7692 {
7693     return offset_norm * (columns->MaxX - columns->MinX);
7694 }
7695 
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)7696 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
7697 {
7698     return offset / (columns->MaxX - columns->MinX);
7699 }
7700 
GetColumnsRectHalfWidth()7701 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
7702 
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)7703 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
7704 {
7705     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
7706     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
7707     ImGuiContext& g = *GImGui;
7708     ImGuiWindow* window = g.CurrentWindow;
7709     IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
7710     IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
7711 
7712     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
7713     x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
7714     if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
7715         x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
7716 
7717     return x;
7718 }
7719 
GetColumnOffset(int column_index)7720 float ImGui::GetColumnOffset(int column_index)
7721 {
7722     ImGuiWindow* window = GetCurrentWindowRead();
7723     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7724     IM_ASSERT(columns != NULL);
7725 
7726     if (column_index < 0)
7727         column_index = columns->Current;
7728     IM_ASSERT(column_index < columns->Columns.Size);
7729 
7730     const float t = columns->Columns[column_index].OffsetNorm;
7731     const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
7732     return x_offset;
7733 }
7734 
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)7735 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
7736 {
7737     if (column_index < 0)
7738         column_index = columns->Current;
7739 
7740     float offset_norm;
7741     if (before_resize)
7742         offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
7743     else
7744         offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
7745     return OffsetNormToPixels(columns, offset_norm);
7746 }
7747 
GetColumnWidth(int column_index)7748 float ImGui::GetColumnWidth(int column_index)
7749 {
7750     ImGuiWindow* window = GetCurrentWindowRead();
7751     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7752     IM_ASSERT(columns != NULL);
7753 
7754     if (column_index < 0)
7755         column_index = columns->Current;
7756     return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
7757 }
7758 
SetColumnOffset(int column_index,float offset)7759 void ImGui::SetColumnOffset(int column_index, float offset)
7760 {
7761     ImGuiContext& g = *GImGui;
7762     ImGuiWindow* window = g.CurrentWindow;
7763     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7764     IM_ASSERT(columns != NULL);
7765 
7766     if (column_index < 0)
7767         column_index = columns->Current;
7768     IM_ASSERT(column_index < columns->Columns.Size);
7769 
7770     const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
7771     const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
7772 
7773     if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
7774         offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
7775     columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
7776 
7777     if (preserve_width)
7778         SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
7779 }
7780 
SetColumnWidth(int column_index,float width)7781 void ImGui::SetColumnWidth(int column_index, float width)
7782 {
7783     ImGuiWindow* window = GetCurrentWindowRead();
7784     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7785     IM_ASSERT(columns != NULL);
7786 
7787     if (column_index < 0)
7788         column_index = columns->Current;
7789     SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
7790 }
7791 
PushColumnClipRect(int column_index)7792 void ImGui::PushColumnClipRect(int column_index)
7793 {
7794     ImGuiWindow* window = GetCurrentWindowRead();
7795     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7796     if (column_index < 0)
7797         column_index = columns->Current;
7798 
7799     PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
7800 }
7801 
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)7802 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
7803 {
7804     for (int n = 0; n < window->ColumnsStorage.Size; n++)
7805         if (window->ColumnsStorage[n].ID == id)
7806             return &window->ColumnsStorage[n];
7807 
7808     window->ColumnsStorage.push_back(ImGuiColumnsSet());
7809     ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
7810     columns->ID = id;
7811     return columns;
7812 }
7813 
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)7814 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
7815 {
7816     ImGuiContext& g = *GImGui;
7817     ImGuiWindow* window = GetCurrentWindow();
7818 
7819     IM_ASSERT(columns_count > 1);
7820     IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
7821 
7822     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
7823     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
7824     PushID(0x11223347 + (str_id ? 0 : columns_count));
7825     ImGuiID id = window->GetID(str_id ? str_id : "columns");
7826     PopID();
7827 
7828     // Acquire storage for the columns set
7829     ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
7830     IM_ASSERT(columns->ID == id);
7831     columns->Current = 0;
7832     columns->Count = columns_count;
7833     columns->Flags = flags;
7834     window->DC.ColumnsSet = columns;
7835 
7836     // Set state for first column
7837     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
7838     columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
7839     columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
7840     columns->StartPosY = window->DC.CursorPos.y;
7841     columns->StartMaxPosX = window->DC.CursorMaxPos.x;
7842     columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
7843     window->DC.ColumnsOffset.x = 0.0f;
7844     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7845 
7846     // Clear data if columns count changed
7847     if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
7848         columns->Columns.resize(0);
7849 
7850     // Initialize defaults
7851     columns->IsFirstFrame = (columns->Columns.Size == 0);
7852     if (columns->Columns.Size == 0)
7853     {
7854         columns->Columns.reserve(columns_count + 1);
7855         for (int n = 0; n < columns_count + 1; n++)
7856         {
7857             ImGuiColumnData column;
7858             column.OffsetNorm = n / (float)columns_count;
7859             columns->Columns.push_back(column);
7860         }
7861     }
7862 
7863     for (int n = 0; n < columns_count; n++)
7864     {
7865         // Compute clipping rectangle
7866         ImGuiColumnData* column = &columns->Columns[n];
7867         float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
7868         float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
7869         column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
7870         column->ClipRect.ClipWith(window->ClipRect);
7871     }
7872 
7873     window->DrawList->ChannelsSplit(columns->Count);
7874     PushColumnClipRect();
7875     PushItemWidth(GetColumnWidth() * 0.65f);
7876 }
7877 
EndColumns()7878 void ImGui::EndColumns()
7879 {
7880     ImGuiContext& g = *GImGui;
7881     ImGuiWindow* window = GetCurrentWindow();
7882     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7883     IM_ASSERT(columns != NULL);
7884 
7885     PopItemWidth();
7886     PopClipRect();
7887     window->DrawList->ChannelsMerge();
7888 
7889     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
7890     window->DC.CursorPos.y = columns->LineMaxY;
7891     if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
7892         window->DC.CursorMaxPos.x = columns->StartMaxPosX;  // Restore cursor max pos, as columns don't grow parent
7893 
7894     // Draw columns borders and handle resize
7895     bool is_being_resized = false;
7896     if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
7897     {
7898         const float y1 = columns->StartPosY;
7899         const float y2 = window->DC.CursorPos.y;
7900         int dragging_column = -1;
7901         for (int n = 1; n < columns->Count; n++)
7902         {
7903             float x = window->Pos.x + GetColumnOffset(n);
7904             const ImGuiID column_id = columns->ID + ImGuiID(n);
7905             const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
7906             const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
7907             KeepAliveID(column_id);
7908             if (IsClippedEx(column_rect, column_id, false))
7909                 continue;
7910 
7911             bool hovered = false, held = false;
7912             if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
7913             {
7914                 ButtonBehavior(column_rect, column_id, &hovered, &held);
7915                 if (hovered || held)
7916                     g.MouseCursor = ImGuiMouseCursor_ResizeEW;
7917                 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
7918                     dragging_column = n;
7919             }
7920 
7921             // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
7922             const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
7923             const float xi = (float)(int)x;
7924             window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
7925         }
7926 
7927         // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
7928         if (dragging_column != -1)
7929         {
7930             if (!columns->IsBeingResized)
7931                 for (int n = 0; n < columns->Count + 1; n++)
7932                     columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
7933             columns->IsBeingResized = is_being_resized = true;
7934             float x = GetDraggedColumnOffset(columns, dragging_column);
7935             SetColumnOffset(dragging_column, x);
7936         }
7937     }
7938     columns->IsBeingResized = is_being_resized;
7939 
7940     window->DC.ColumnsSet = NULL;
7941     window->DC.ColumnsOffset.x = 0.0f;
7942     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7943 }
7944 
7945 // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
Columns(int columns_count,const char * id,bool border)7946 void ImGui::Columns(int columns_count, const char* id, bool border)
7947 {
7948     ImGuiWindow* window = GetCurrentWindow();
7949     IM_ASSERT(columns_count >= 1);
7950 
7951     ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
7952     //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
7953     if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
7954         return;
7955 
7956     if (window->DC.ColumnsSet != NULL)
7957         EndColumns();
7958 
7959     if (columns_count != 1)
7960         BeginColumns(id, columns_count, flags);
7961 }
7962 
7963 //-----------------------------------------------------------------------------
7964 // [SECTION] DRAG AND DROP
7965 //-----------------------------------------------------------------------------
7966 
ClearDragDrop()7967 void ImGui::ClearDragDrop()
7968 {
7969     ImGuiContext& g = *GImGui;
7970     g.DragDropActive = false;
7971     g.DragDropPayload.Clear();
7972     g.DragDropAcceptFlags = 0;
7973     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
7974     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
7975     g.DragDropAcceptFrameCount = -1;
7976 
7977     g.DragDropPayloadBufHeap.clear();
7978     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
7979 }
7980 
7981 // Call when current ID is active.
7982 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
BeginDragDropSource(ImGuiDragDropFlags flags)7983 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
7984 {
7985     ImGuiContext& g = *GImGui;
7986     ImGuiWindow* window = g.CurrentWindow;
7987 
7988     bool source_drag_active = false;
7989     ImGuiID source_id = 0;
7990     ImGuiID source_parent_id = 0;
7991     int mouse_button = 0;
7992     if (!(flags & ImGuiDragDropFlags_SourceExtern))
7993     {
7994         source_id = window->DC.LastItemId;
7995         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
7996             return false;
7997         if (g.IO.MouseDown[mouse_button] == false)
7998             return false;
7999 
8000         if (source_id == 0)
8001         {
8002             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8003             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8004             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8005             {
8006                 IM_ASSERT(0);
8007                 return false;
8008             }
8009 
8010             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8011             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8012             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8013             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8014             bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8015             if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8016                 return false;
8017             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8018             if (is_hovered)
8019                 SetHoveredID(source_id);
8020             if (is_hovered && g.IO.MouseClicked[mouse_button])
8021             {
8022                 SetActiveID(source_id, window);
8023                 FocusWindow(window);
8024             }
8025             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8026                 g.ActiveIdAllowOverlap = is_hovered;
8027         }
8028         else
8029         {
8030             g.ActiveIdAllowOverlap = false;
8031         }
8032         if (g.ActiveId != source_id)
8033             return false;
8034         source_parent_id = window->IDStack.back();
8035         source_drag_active = IsMouseDragging(mouse_button);
8036     }
8037     else
8038     {
8039         window = NULL;
8040         source_id = ImHash("#SourceExtern", 0);
8041         source_drag_active = true;
8042     }
8043 
8044     if (source_drag_active)
8045     {
8046         if (!g.DragDropActive)
8047         {
8048             IM_ASSERT(source_id != 0);
8049             ClearDragDrop();
8050             ImGuiPayload& payload = g.DragDropPayload;
8051             payload.SourceId = source_id;
8052             payload.SourceParentId = source_parent_id;
8053             g.DragDropActive = true;
8054             g.DragDropSourceFlags = flags;
8055             g.DragDropMouseButton = mouse_button;
8056         }
8057         g.DragDropSourceFrameCount = g.FrameCount;
8058         g.DragDropWithinSourceOrTarget = true;
8059 
8060         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8061         {
8062             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8063             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8064             BeginTooltip();
8065             if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8066             {
8067                 ImGuiWindow* tooltip_window = g.CurrentWindow;
8068                 tooltip_window->SkipItems = true;
8069                 tooltip_window->HiddenFramesRegular = 1;
8070             }
8071         }
8072 
8073         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8074             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8075 
8076         return true;
8077     }
8078     return false;
8079 }
8080 
EndDragDropSource()8081 void ImGui::EndDragDropSource()
8082 {
8083     ImGuiContext& g = *GImGui;
8084     IM_ASSERT(g.DragDropActive);
8085     IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8086 
8087     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8088         EndTooltip();
8089 
8090     // Discard the drag if have not called SetDragDropPayload()
8091     if (g.DragDropPayload.DataFrameCount == -1)
8092         ClearDragDrop();
8093     g.DragDropWithinSourceOrTarget = false;
8094 }
8095 
8096 // Use 'cond' to choose to submit payload on drag start or every frame
SetDragDropPayload(const char * type,const void * data,size_t data_size,ImGuiCond cond)8097 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8098 {
8099     ImGuiContext& g = *GImGui;
8100     ImGuiPayload& payload = g.DragDropPayload;
8101     if (cond == 0)
8102         cond = ImGuiCond_Always;
8103 
8104     IM_ASSERT(type != NULL);
8105     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8106     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8107     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8108     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
8109 
8110     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8111     {
8112         // Copy payload
8113         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8114         g.DragDropPayloadBufHeap.resize(0);
8115         if (data_size > sizeof(g.DragDropPayloadBufLocal))
8116         {
8117             // Store in heap
8118             g.DragDropPayloadBufHeap.resize((int)data_size);
8119             payload.Data = g.DragDropPayloadBufHeap.Data;
8120             memcpy(payload.Data, data, data_size);
8121         }
8122         else if (data_size > 0)
8123         {
8124             // Store locally
8125             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8126             payload.Data = g.DragDropPayloadBufLocal;
8127             memcpy(payload.Data, data, data_size);
8128         }
8129         else
8130         {
8131             payload.Data = NULL;
8132         }
8133         payload.DataSize = (int)data_size;
8134     }
8135     payload.DataFrameCount = g.FrameCount;
8136 
8137     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8138 }
8139 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8140 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8141 {
8142     ImGuiContext& g = *GImGui;
8143     if (!g.DragDropActive)
8144         return false;
8145 
8146     ImGuiWindow* window = g.CurrentWindow;
8147     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8148         return false;
8149     IM_ASSERT(id != 0);
8150     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8151         return false;
8152     if (window->SkipItems)
8153         return false;
8154 
8155     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8156     g.DragDropTargetRect = bb;
8157     g.DragDropTargetId = id;
8158     g.DragDropWithinSourceOrTarget = true;
8159     return true;
8160 }
8161 
8162 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8163 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8164 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8165 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8166 bool ImGui::BeginDragDropTarget()
8167 {
8168     ImGuiContext& g = *GImGui;
8169     if (!g.DragDropActive)
8170         return false;
8171 
8172     ImGuiWindow* window = g.CurrentWindow;
8173     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8174         return false;
8175     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8176         return false;
8177 
8178     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8179     ImGuiID id = window->DC.LastItemId;
8180     if (id == 0)
8181         id = window->GetIDFromRectangle(display_rect);
8182     if (g.DragDropPayload.SourceId == id)
8183         return false;
8184 
8185     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8186     g.DragDropTargetRect = display_rect;
8187     g.DragDropTargetId = id;
8188     g.DragDropWithinSourceOrTarget = true;
8189     return true;
8190 }
8191 
IsDragDropPayloadBeingAccepted()8192 bool ImGui::IsDragDropPayloadBeingAccepted()
8193 {
8194     ImGuiContext& g = *GImGui;
8195     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8196 }
8197 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8198 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8199 {
8200     ImGuiContext& g = *GImGui;
8201     ImGuiWindow* window = g.CurrentWindow;
8202     ImGuiPayload& payload = g.DragDropPayload;
8203     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8204     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
8205     if (type != NULL && !payload.IsDataType(type))
8206         return NULL;
8207 
8208     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8209     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8210     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8211     ImRect r = g.DragDropTargetRect;
8212     float r_surface = r.GetWidth() * r.GetHeight();
8213     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8214     {
8215         g.DragDropAcceptFlags = flags;
8216         g.DragDropAcceptIdCurr = g.DragDropTargetId;
8217         g.DragDropAcceptIdCurrRectSurface = r_surface;
8218     }
8219 
8220     // Render default drop visuals
8221     payload.Preview = was_accepted_previously;
8222     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8223     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8224     {
8225         // FIXME-DRAG: Settle on a proper default visuals for drop target.
8226         r.Expand(3.5f);
8227         bool push_clip_rect = !window->ClipRect.Contains(r);
8228         if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8229         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8230         if (push_clip_rect) window->DrawList->PopClipRect();
8231     }
8232 
8233     g.DragDropAcceptFrameCount = g.FrameCount;
8234     payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
8235     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8236         return NULL;
8237 
8238     return &payload;
8239 }
8240 
8241 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8242 void ImGui::EndDragDropTarget()
8243 {
8244     ImGuiContext& g = *GImGui;
8245     IM_ASSERT(g.DragDropActive);
8246     IM_ASSERT(g.DragDropWithinSourceOrTarget);
8247     g.DragDropWithinSourceOrTarget = false;
8248 }
8249 
8250 //-----------------------------------------------------------------------------
8251 // [SECTION] LOGGING/CAPTURING
8252 //-----------------------------------------------------------------------------
8253 
8254 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8255 void ImGui::LogText(const char* fmt, ...)
8256 {
8257     ImGuiContext& g = *GImGui;
8258     if (!g.LogEnabled)
8259         return;
8260 
8261     va_list args;
8262     va_start(args, fmt);
8263     if (g.LogFile)
8264         vfprintf(g.LogFile, fmt, args);
8265     else
8266         g.LogClipboard.appendfv(fmt, args);
8267     va_end(args);
8268 }
8269 
8270 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
8271 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)8272 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8273 {
8274     ImGuiContext& g = *GImGui;
8275     ImGuiWindow* window = g.CurrentWindow;
8276 
8277     if (!text_end)
8278         text_end = FindRenderedTextEnd(text, text_end);
8279 
8280     const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8281     if (ref_pos)
8282         window->DC.LogLinePosY = ref_pos->y;
8283 
8284     const char* text_remaining = text;
8285     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
8286         g.LogStartDepth = window->DC.TreeDepth;
8287     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8288     for (;;)
8289     {
8290         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8291         const char* line_end = text_remaining;
8292         while (line_end < text_end)
8293             if (*line_end == '\n')
8294                 break;
8295             else
8296                 line_end++;
8297         if (line_end >= text_end)
8298             line_end = NULL;
8299 
8300         const bool is_first_line = (text == text_remaining);
8301         bool is_last_line = false;
8302         if (line_end == NULL)
8303         {
8304             is_last_line = true;
8305             line_end = text_end;
8306         }
8307         if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
8308         {
8309             const int char_count = (int)(line_end - text_remaining);
8310             if (log_new_line || !is_first_line)
8311                 LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
8312             else
8313                 LogText(" %.*s", char_count, text_remaining);
8314         }
8315 
8316         if (is_last_line)
8317             break;
8318         text_remaining = line_end + 1;
8319     }
8320 }
8321 
8322 // Start logging ImGui output to TTY
LogToTTY(int max_depth)8323 void ImGui::LogToTTY(int max_depth)
8324 {
8325     ImGuiContext& g = *GImGui;
8326     if (g.LogEnabled)
8327         return;
8328     ImGuiWindow* window = g.CurrentWindow;
8329 
8330     IM_ASSERT(g.LogFile == NULL);
8331     g.LogFile = stdout;
8332     g.LogEnabled = true;
8333     g.LogStartDepth = window->DC.TreeDepth;
8334     if (max_depth >= 0)
8335         g.LogAutoExpandMaxDepth = max_depth;
8336 }
8337 
8338 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)8339 void ImGui::LogToFile(int max_depth, const char* filename)
8340 {
8341     ImGuiContext& g = *GImGui;
8342     if (g.LogEnabled)
8343         return;
8344     ImGuiWindow* window = g.CurrentWindow;
8345 
8346     if (!filename)
8347     {
8348         filename = g.IO.LogFilename;
8349         if (!filename)
8350             return;
8351     }
8352 
8353     IM_ASSERT(g.LogFile == NULL);
8354     g.LogFile = ImFileOpen(filename, "ab");
8355     if (!g.LogFile)
8356     {
8357         IM_ASSERT(g.LogFile != NULL); // Consider this an error
8358         return;
8359     }
8360     g.LogEnabled = true;
8361     g.LogStartDepth = window->DC.TreeDepth;
8362     if (max_depth >= 0)
8363         g.LogAutoExpandMaxDepth = max_depth;
8364 }
8365 
8366 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)8367 void ImGui::LogToClipboard(int max_depth)
8368 {
8369     ImGuiContext& g = *GImGui;
8370     if (g.LogEnabled)
8371         return;
8372     ImGuiWindow* window = g.CurrentWindow;
8373 
8374     IM_ASSERT(g.LogFile == NULL);
8375     g.LogFile = NULL;
8376     g.LogEnabled = true;
8377     g.LogStartDepth = window->DC.TreeDepth;
8378     if (max_depth >= 0)
8379         g.LogAutoExpandMaxDepth = max_depth;
8380 }
8381 
LogFinish()8382 void ImGui::LogFinish()
8383 {
8384     ImGuiContext& g = *GImGui;
8385     if (!g.LogEnabled)
8386         return;
8387 
8388     LogText(IM_NEWLINE);
8389     if (g.LogFile != NULL)
8390     {
8391         if (g.LogFile == stdout)
8392             fflush(g.LogFile);
8393         else
8394             fclose(g.LogFile);
8395         g.LogFile = NULL;
8396     }
8397     if (g.LogClipboard.size() > 1)
8398     {
8399         SetClipboardText(g.LogClipboard.begin());
8400         g.LogClipboard.clear();
8401     }
8402     g.LogEnabled = false;
8403 }
8404 
8405 // Helper to display logging buttons
LogButtons()8406 void ImGui::LogButtons()
8407 {
8408     ImGuiContext& g = *GImGui;
8409 
8410     PushID("LogButtons");
8411     const bool log_to_tty = Button("Log To TTY"); SameLine();
8412     const bool log_to_file = Button("Log To File"); SameLine();
8413     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
8414     PushItemWidth(80.0f);
8415     PushAllowKeyboardFocus(false);
8416     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8417     PopAllowKeyboardFocus();
8418     PopItemWidth();
8419     PopID();
8420 
8421     // Start logging at the end of the function so that the buttons don't appear in the log
8422     if (log_to_tty)
8423         LogToTTY(g.LogAutoExpandMaxDepth);
8424     if (log_to_file)
8425         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8426     if (log_to_clipboard)
8427         LogToClipboard(g.LogAutoExpandMaxDepth);
8428 }
8429 
8430 //-----------------------------------------------------------------------------
8431 // [SECTION] SETTINGS
8432 //-----------------------------------------------------------------------------
8433 
MarkIniSettingsDirty()8434 void ImGui::MarkIniSettingsDirty()
8435 {
8436     ImGuiContext& g = *GImGui;
8437     if (g.SettingsDirtyTimer <= 0.0f)
8438         g.SettingsDirtyTimer = g.IO.IniSavingRate;
8439 }
8440 
MarkIniSettingsDirty(ImGuiWindow * window)8441 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8442 {
8443     ImGuiContext& g = *GImGui;
8444     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8445         if (g.SettingsDirtyTimer <= 0.0f)
8446             g.SettingsDirtyTimer = g.IO.IniSavingRate;
8447 }
8448 
CreateNewWindowSettings(const char * name)8449 static ImGuiWindowSettings* CreateNewWindowSettings(const char* name)
8450 {
8451     ImGuiContext& g = *GImGui;
8452     g.SettingsWindows.push_back(ImGuiWindowSettings());
8453     ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8454     settings->Name = ImStrdup(name);
8455     settings->ID = ImHash(name, 0);
8456     return settings;
8457 }
8458 
FindWindowSettings(ImGuiID id)8459 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8460 {
8461     ImGuiContext& g = *GImGui;
8462     for (int i = 0; i != g.SettingsWindows.Size; i++)
8463         if (g.SettingsWindows[i].ID == id)
8464             return &g.SettingsWindows[i];
8465     return NULL;
8466 }
8467 
LoadIniSettingsFromDisk(const char * ini_filename)8468 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8469 {
8470     size_t file_data_size = 0;
8471     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
8472     if (!file_data)
8473         return;
8474     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
8475     ImGui::MemFree(file_data);
8476 }
8477 
FindSettingsHandler(const char * type_name)8478 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8479 {
8480     ImGuiContext& g = *GImGui;
8481     const ImGuiID type_hash = ImHash(type_name, 0, 0);
8482     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8483         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
8484             return &g.SettingsHandlers[handler_n];
8485     return NULL;
8486 }
8487 
8488 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)8489 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
8490 {
8491     ImGuiContext& g = *GImGui;
8492     IM_ASSERT(g.Initialized);
8493     IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
8494 
8495     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
8496     // 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..
8497     if (ini_size == 0)
8498         ini_size = strlen(ini_data);
8499     char* buf = (char*)ImGui::MemAlloc(ini_size + 1);
8500     char* buf_end = buf + ini_size;
8501     memcpy(buf, ini_data, ini_size);
8502     buf[ini_size] = 0;
8503 
8504     void* entry_data = NULL;
8505     ImGuiSettingsHandler* entry_handler = NULL;
8506 
8507     char* line_end = NULL;
8508     for (char* line = buf; line < buf_end; line = line_end + 1)
8509     {
8510         // Skip new lines markers, then find end of the line
8511         while (*line == '\n' || *line == '\r')
8512             line++;
8513         line_end = line;
8514         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
8515             line_end++;
8516         line_end[0] = 0;
8517         if (line[0] == ';')
8518             continue;
8519         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
8520         {
8521             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
8522             line_end[-1] = 0;
8523             const char* name_end = line_end - 1;
8524             const char* type_start = line + 1;
8525             char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
8526             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
8527             if (!type_end || !name_start)
8528             {
8529                 name_start = type_start; // Import legacy entries that have no type
8530                 type_start = "Window";
8531             }
8532             else
8533             {
8534                 *type_end = 0; // Overwrite first ']'
8535                 name_start++;  // Skip second '['
8536             }
8537             entry_handler = FindSettingsHandler(type_start);
8538             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
8539         }
8540         else if (entry_handler != NULL && entry_data != NULL)
8541         {
8542             // Let type handler parse the line
8543             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
8544         }
8545     }
8546     ImGui::MemFree(buf);
8547     g.SettingsLoaded = true;
8548 }
8549 
SaveIniSettingsToDisk(const char * ini_filename)8550 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
8551 {
8552     ImGuiContext& g = *GImGui;
8553     g.SettingsDirtyTimer = 0.0f;
8554     if (!ini_filename)
8555         return;
8556 
8557     size_t ini_data_size = 0;
8558     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
8559     FILE* f = ImFileOpen(ini_filename, "wt");
8560     if (!f)
8561         return;
8562     fwrite(ini_data, sizeof(char), ini_data_size, f);
8563     fclose(f);
8564 }
8565 
8566 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)8567 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
8568 {
8569     ImGuiContext& g = *GImGui;
8570     g.SettingsDirtyTimer = 0.0f;
8571     g.SettingsIniData.Buf.resize(0);
8572     g.SettingsIniData.Buf.push_back(0);
8573     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8574     {
8575         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
8576         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
8577     }
8578     if (out_size)
8579         *out_size = (size_t)g.SettingsIniData.size();
8580     return g.SettingsIniData.c_str();
8581 }
8582 
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)8583 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
8584 {
8585     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
8586     if (!settings)
8587         settings = CreateNewWindowSettings(name);
8588     return (void*)settings;
8589 }
8590 
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)8591 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
8592 {
8593     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
8594     float x, y;
8595     int i;
8596     if (sscanf(line, "Pos=%f,%f", &x, &y) == 2)         settings->Pos = ImVec2(x, y);
8597     else if (sscanf(line, "Size=%f,%f", &x, &y) == 2)   settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
8598     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
8599 }
8600 
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)8601 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
8602 {
8603     // Gather data from windows that were active during this session
8604     // (if a window wasn't opened in this session we preserve its settings)
8605     ImGuiContext& g = *imgui_ctx;
8606     for (int i = 0; i != g.Windows.Size; i++)
8607     {
8608         ImGuiWindow* window = g.Windows[i];
8609         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
8610             continue;
8611 
8612         ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
8613         if (!settings)
8614         {
8615             settings = CreateNewWindowSettings(window->Name);
8616             window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
8617         }
8618         IM_ASSERT(settings->ID == window->ID);
8619         settings->Pos = window->Pos;
8620         settings->Size = window->SizeFull;
8621         settings->Collapsed = window->Collapsed;
8622     }
8623 
8624     // Write to text buffer
8625     buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
8626     for (int i = 0; i != g.SettingsWindows.Size; i++)
8627     {
8628         const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
8629         if (settings->Pos.x == FLT_MAX)
8630             continue;
8631         const char* name = settings->Name;
8632         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
8633             name = p;
8634         buf->appendf("[%s][%s]\n", handler->TypeName, name);
8635         buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
8636         buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
8637         buf->appendf("Collapsed=%d\n", settings->Collapsed);
8638         buf->appendf("\n");
8639     }
8640 }
8641 
8642 //-----------------------------------------------------------------------------
8643 // [SECTION] PLATFORM DEPENDENT HELPERS
8644 //-----------------------------------------------------------------------------
8645 
8646 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
8647 #ifndef WIN32_LEAN_AND_MEAN
8648 #define WIN32_LEAN_AND_MEAN
8649 #endif
8650 #ifndef __MINGW32__
8651 #include <Windows.h>
8652 #else
8653 #include <windows.h>
8654 #endif
8655 #endif
8656 
8657 // Win32 API clipboard implementation
8658 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
8659 
8660 #ifdef _MSC_VER
8661 #pragma comment(lib, "user32")
8662 #endif
8663 
GetClipboardTextFn_DefaultImpl(void *)8664 static const char* GetClipboardTextFn_DefaultImpl(void*)
8665 {
8666     static ImVector<char> buf_local;
8667     buf_local.clear();
8668     if (!::OpenClipboard(NULL))
8669         return NULL;
8670     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
8671     if (wbuf_handle == NULL)
8672     {
8673         ::CloseClipboard();
8674         return NULL;
8675     }
8676     if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
8677     {
8678         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
8679         buf_local.resize(buf_len);
8680         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
8681     }
8682     ::GlobalUnlock(wbuf_handle);
8683     ::CloseClipboard();
8684     return buf_local.Data;
8685 }
8686 
SetClipboardTextFn_DefaultImpl(void *,const char * text)8687 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8688 {
8689     if (!::OpenClipboard(NULL))
8690         return;
8691     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
8692     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
8693     if (wbuf_handle == NULL)
8694     {
8695         ::CloseClipboard();
8696         return;
8697     }
8698     ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
8699     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
8700     ::GlobalUnlock(wbuf_handle);
8701     ::EmptyClipboard();
8702     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
8703         ::GlobalFree(wbuf_handle);
8704     ::CloseClipboard();
8705 }
8706 
8707 #else
8708 
8709 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)8710 static const char* GetClipboardTextFn_DefaultImpl(void*)
8711 {
8712     ImGuiContext& g = *GImGui;
8713     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
8714 }
8715 
8716 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)8717 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8718 {
8719     ImGuiContext& g = *GImGui;
8720     g.PrivateClipboard.clear();
8721     const char* text_end = text + strlen(text);
8722     g.PrivateClipboard.resize((int)(text_end - text) + 1);
8723     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
8724     g.PrivateClipboard[(int)(text_end - text)] = 0;
8725 }
8726 
8727 #endif
8728 
8729 // Win32 API IME support (for Asian languages, etc.)
8730 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
8731 
8732 #include <imm.h>
8733 #ifdef _MSC_VER
8734 #pragma comment(lib, "imm32")
8735 #endif
8736 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)8737 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
8738 {
8739     // Notify OS Input Method Editor of text input position
8740     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
8741         if (HIMC himc = ::ImmGetContext(hwnd))
8742         {
8743             COMPOSITIONFORM cf;
8744             cf.ptCurrentPos.x = x;
8745             cf.ptCurrentPos.y = y;
8746             cf.dwStyle = CFS_FORCE_POSITION;
8747             ::ImmSetCompositionWindow(himc, &cf);
8748             ::ImmReleaseContext(hwnd, himc);
8749         }
8750 }
8751 
8752 #else
8753 
ImeSetInputScreenPosFn_DefaultImpl(int,int)8754 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
8755 
8756 #endif
8757 
8758 //-----------------------------------------------------------------------------
8759 // [SECTION] METRICS/DEBUG WINDOW
8760 //-----------------------------------------------------------------------------
8761 
ShowMetricsWindow(bool * p_open)8762 void ImGui::ShowMetricsWindow(bool* p_open)
8763 {
8764     if (!ImGui::Begin("ImGui Metrics", p_open))
8765     {
8766         ImGui::End();
8767         return;
8768     }
8769     static bool show_draw_cmd_clip_rects = true;
8770     static bool show_window_begin_order = false;
8771     ImGuiIO& io = ImGui::GetIO();
8772     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
8773     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
8774     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
8775     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
8776     ImGui::Text("%d allocations", io.MetricsActiveAllocations);
8777     ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
8778     ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order);
8779 
8780     ImGui::Separator();
8781 
8782     struct Funcs
8783     {
8784         static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
8785         {
8786             bool node_open = ImGui::TreeNode(draw_list, "%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);
8787             if (draw_list == ImGui::GetWindowDrawList())
8788             {
8789                 ImGui::SameLine();
8790                 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
8791                 if (node_open) ImGui::TreePop();
8792                 return;
8793             }
8794 
8795             ImDrawList* overlay_draw_list = GetOverlayDrawList(); // Render additional visuals into the top-most draw list
8796             if (window && IsItemHovered())
8797                 overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
8798             if (!node_open)
8799                 return;
8800 
8801             int elem_offset = 0;
8802             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
8803             {
8804                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
8805                     continue;
8806                 if (pcmd->UserCallback)
8807                 {
8808                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
8809                     continue;
8810                 }
8811                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
8812                 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "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);
8813                 if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
8814                 {
8815                     ImRect clip_rect = pcmd->ClipRect;
8816                     ImRect vtxs_rect;
8817                     for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
8818                         vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
8819                     clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
8820                     vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
8821                 }
8822                 if (!pcmd_node_open)
8823                     continue;
8824 
8825                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
8826                 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
8827                 while (clipper.Step())
8828                     for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
8829                     {
8830                         char buf[300];
8831                         char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
8832                         ImVec2 triangles_pos[3];
8833                         for (int n = 0; n < 3; n++, vtx_i++)
8834                         {
8835                             ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
8836                             triangles_pos[n] = v.pos;
8837                             buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%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);
8838                         }
8839                         ImGui::Selectable(buf, false);
8840                         if (ImGui::IsItemHovered())
8841                         {
8842                             ImDrawListFlags backup_flags = overlay_draw_list->Flags;
8843                             overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
8844                             overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
8845                             overlay_draw_list->Flags = backup_flags;
8846                         }
8847                     }
8848                 ImGui::TreePop();
8849             }
8850             ImGui::TreePop();
8851         }
8852 
8853         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
8854         {
8855             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
8856                 return;
8857             for (int i = 0; i < windows.Size; i++)
8858                 Funcs::NodeWindow(windows[i], "Window");
8859             ImGui::TreePop();
8860         }
8861 
8862         static void NodeWindow(ImGuiWindow* window, const char* label)
8863         {
8864             if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
8865                 return;
8866             ImGuiWindowFlags flags = window->Flags;
8867             NodeDrawList(window, window->DrawList, "DrawList");
8868             ImGui::BulletText("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);
8869             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s..)", flags,
8870                 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip)   ? "Tooltip "   : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
8871                 (flags & ImGuiWindowFlags_Modal)       ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
8872                 (flags & ImGuiWindowFlags_NoInputs)    ? "NoInputs":"", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
8873             ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window));
8874             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
8875             ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
8876             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
8877             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
8878             if (!window->NavRectRel[0].IsInverted())
8879                 ImGui::BulletText("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);
8880             else
8881                 ImGui::BulletText("NavRectRel[0]: <None>");
8882             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
8883             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
8884             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
8885             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
8886             {
8887                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
8888                 {
8889                     const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
8890                     if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
8891                     {
8892                         ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
8893                         for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
8894                             ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
8895                         ImGui::TreePop();
8896                     }
8897                 }
8898                 ImGui::TreePop();
8899             }
8900             ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
8901             ImGui::TreePop();
8902         }
8903     };
8904 
8905     // Access private state, we are going to display the draw lists from last frame
8906     ImGuiContext& g = *GImGui;
8907     Funcs::NodeWindows(g.Windows, "Windows");
8908     if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
8909     {
8910         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
8911             Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
8912         ImGui::TreePop();
8913     }
8914     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
8915     {
8916         for (int i = 0; i < g.OpenPopupStack.Size; i++)
8917         {
8918             ImGuiWindow* window = g.OpenPopupStack[i].Window;
8919             ImGui::BulletText("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" : "");
8920         }
8921         ImGui::TreePop();
8922     }
8923     if (ImGui::TreeNode("Internal state"))
8924     {
8925         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
8926         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
8927         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
8928         ImGui::Text("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
8929         ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
8930         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
8931         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
8932         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
8933         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
8934         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
8935         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
8936         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
8937         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
8938         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
8939         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
8940         ImGui::TreePop();
8941     }
8942 
8943 
8944     if (g.IO.KeyCtrl && show_window_begin_order)
8945     {
8946         for (int n = 0; n < g.Windows.Size; n++)
8947         {
8948             ImGuiWindow* window = g.Windows[n];
8949             if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
8950                 continue;
8951             char buf[32];
8952             ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
8953             float font_size = ImGui::GetFontSize() * 2;
8954             ImDrawList* overlay_draw_list = GetOverlayDrawList();
8955             overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
8956             overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf);
8957         }
8958     }
8959     ImGui::End();
8960 }
8961 
8962 //-----------------------------------------------------------------------------
8963 
8964 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
8965 // 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.
8966 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
8967 #include "imgui_user.inl"
8968 #endif
8969 
8970 //-----------------------------------------------------------------------------
8971