1 // dear imgui, v1.66b
2 // (main code and documentation)
3 
4 // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Technical Support for Getting Started https://discourse.dearimgui.org/c/getting-started
9 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
10 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
11 // This library is free but I need your support to sustain development and maintenance.
12 // If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui
13 
14 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
15 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
16 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
17 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
18 // to a better solution or official support for them.
19 
20 /*
21 
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 superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
133  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
134  - You can learn about immediate-mode GUI principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
135    See README.md for more links describing the IMGUI paradigm. Dear ImGui is an implementation of the IMGUI paradigm.
136 
137  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
138 
139  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
140  - Or maintain your own branch where you have imconfig.h modified.
141  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
142    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
143    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
144    likely be a comment about it. Please report any issue to the GitHub page!
145  - Try to keep your copy of dear imgui reasonably up to date.
146 
147  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
148 
149  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
150  - Add the Dear ImGui source files to your projects or using your preferred build system.
151    It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
152  - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating imgui types with your own maths types.
153  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
154  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
155    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
156    phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
157  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
158  - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.
159 
160  HOW A SIMPLE APPLICATION MAY LOOK LIKE
161  EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder)
162 
163      // Application init: create a dear imgui context, setup some options, load fonts
164      ImGui::CreateContext();
165      ImGuiIO& io = ImGui::GetIO();
166      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
167      // TODO: Fill optional fields of the io structure later.
168      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
169 
170      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
171      ImGui_ImplWin32_Init(hwnd);
172      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
173 
174      // Application main loop
175      while (true)
176      {
177          // Feed inputs to dear imgui, start new frame
178          ImGui_ImplDX11_NewFrame();
179          ImGui_ImplWin32_NewFrame();
180          ImGui::NewFrame();
181 
182          // Any application code here
183          ImGui::Text("Hello, world!");
184 
185          // Render dear imgui into screen
186          ImGui::Render();
187          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
188          g_pSwapChain->Present(1, 0);
189      }
190 
191      // Shutdown
192      ImGui_ImplDX11_Shutdown();
193      ImGui_ImplWin32_Shutdown();
194      ImGui::DestroyContext();
195 
196  HOW A SIMPLE APPLICATION MAY LOOK LIKE
197  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
198 
199      // Application init: create a dear imgui context, setup some options, load fonts
200      ImGui::CreateContext();
201      ImGuiIO& io = ImGui::GetIO();
202      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
203      // TODO: Fill optional fields of the io structure later.
204      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
205 
206      // Build and load the texture atlas into a texture
207      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
208      int width, height;
209      unsigned char* pixels = NULL;
210      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
211 
212      // At this point you've got the texture data and you need to upload that your your graphic system:
213      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
214      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
215      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
216      io.Fonts->TexID = (void*)texture;
217 
218      // Application main loop
219      while (true)
220      {
221         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
222         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
223         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
224         io.DisplaySize.x = 1920.0f;             // set the current display width
225         io.DisplaySize.y = 1280.0f;             // set the current display height here
226         io.MousePos = my_mouse_pos;             // set the mouse position
227         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
228         io.MouseDown[1] = my_mouse_buttons[1];
229 
230         // Call NewFrame(), after this point you can use ImGui::* functions anytime
231         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
232         ImGui::NewFrame();
233 
234         // Most of your application code here
235         ImGui::Text("Hello, world!");
236         MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
237         MyGameRender(); // may use any ImGui functions as well!
238 
239         // Render imgui, swap buffers
240         // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
241         ImGui::EndFrame();
242         ImGui::Render();
243         ImDrawData* draw_data = ImGui::GetDrawData();
244         MyImGuiRenderFunction(draw_data);
245         SwapBuffers();
246      }
247 
248      // Shutdown
249      ImGui::DestroyContext();
250 
251  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
252 
253     void void MyImGuiRenderFunction(ImDrawData* draw_data)
254     {
255        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
256        // TODO: Setup viewport using draw_data->DisplaySize
257        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
258        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
259        for (int n = 0; n < draw_data->CmdListsCount; n++)
260        {
261           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by ImGui
262           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by ImGui
263           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
264           {
265              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
266              if (pcmd->UserCallback)
267              {
268                  pcmd->UserCallback(cmd_list, pcmd);
269              }
270              else
271              {
272                  // The texture for the draw call is specified by pcmd->TextureId.
273                  // The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization.
274                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
275 
276                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
277                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
278                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
279                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
280                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
281                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
282                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
283                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
284                  ImVec2 pos = draw_data->DisplayPos;
285                  MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y));
286 
287                  // Render 'pcmd->ElemCount/3' indexed triangles.
288                  // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
289                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
290              }
291              idx_buffer += pcmd->ElemCount;
292           }
293        }
294     }
295 
296  - The examples/ folders contains many actual implementation of the pseudo-codes above.
297  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
298    They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs
299    from the rest of your application. In every cases you need to pass on the inputs to imgui. Refer to the FAQ for more information.
300  - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues!
301 
302  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
303 
304  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
305  - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
306  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
307  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
308  - Gamepad:
309     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
310     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
311       Note that io.NavInputs[] is cleared by EndFrame().
312     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
313          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
314     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
315       Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
316     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
317     - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo
318       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
319  - Keyboard:
320     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
321       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
322     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
323       will be set. For more advanced uses, you may want to read from:
324        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
325        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
326        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
327       Please reach out if you think the game vs navigation input sharing could be improved.
328  - Mouse:
329     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
330     - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard.
331     - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.
332       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
333       When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
334       When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
335       (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
336       (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want
337        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
338 
339 
340  API BREAKING CHANGES
341  ====================
342 
343  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
344  Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.
345  When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
346  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
347 
348  - 2018/10/12 (1.66) - Renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
349  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
350  - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h.
351                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
352  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
353  - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp.
354                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
355                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
356  - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent).
357  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
358  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
359  - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature.
360  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
361  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
362  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
363  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
364  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
365  - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more.
366                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
367                        To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.
368                        If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them.
369  - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format",
370                        consistent with other functions. Kept redirection functions (will obsolete).
371  - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.
372  - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
373  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
374  - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
375  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
376  - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.
377  - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.
378  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
379                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
380                        - removed Shutdown() function, as DestroyContext() serve this purpose.
381                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
382                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
383                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
384  - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.
385  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
386  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
387  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
388  - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.
389  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
390  - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags
391  - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.
392  - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.
393  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
394  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
395                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
396  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
397  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
398  - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.
399  - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
400                        Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
401  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
402  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
403  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
404  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
405  - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
406  - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
407  - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
408                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
409  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
410  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
411  - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
412  - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
413  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
414                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
415                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
416  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
417  - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
418  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
419  - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
420  - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
421  - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
422  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
423  - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
424                      - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
425                      - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))'
426  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
427  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
428  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
429  - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
430  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
431  - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
432  - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
433  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
434                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
435                        If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
436                        This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
437                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
438                            {
439                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
440                                return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a);
441                            }
442                        If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
443  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
444  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
445  - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
446  - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
447  - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337).
448  - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
449  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
450  - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
451  - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
452  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
453  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
454  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
455                        GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
456                        GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
457  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
458  - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
459  - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
460  - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
461                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
462  - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
463                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
464                      - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
465                      - the signature of the io.RenderDrawListsFn handler has changed!
466                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
467                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
468                          argument:   'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
469                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
470                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
471                      - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
472                      - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
473                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
474  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
475  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
476  - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
477  - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
478  - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
479  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
480  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
481  - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
482  - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
483  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
484  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
485  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
486  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
487  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
488  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
489  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
490  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
491  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
492  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
493  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
494  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
495  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
496  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
497  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
498  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
499  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
500               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
501                        font init:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>
502                        became:     unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier;
503                        you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
504                        it is now recommended that you sample the font texture with bilinear interpolation.
505               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
506               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
507               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
508  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
509  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
510  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
511  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
512  - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
513  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
514  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
515  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
516  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
517  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
518  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
519 
520 
521  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
522  ======================================
523 
524  Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
525  A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
526     - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
527     - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
528     - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
529     Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
530      This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
531     Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
532      It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
533      Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
534      perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
535     Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
536      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
537      were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
538 
539  Q: How can I display an image? What is ImTextureID, how does it works?
540  A: Short explanation:
541     - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures.
542     - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value.
543     - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
544       Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
545 
546     Long explanation:
547     - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
548       At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code
549       to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.).
550     - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
551       We carry the information to identify a "texture" in the ImTextureID type.
552       ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice.
553       Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
554     - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
555       an image from the end-user perspective. This is what the _examples_ rendering functions are using:
556 
557          OpenGL:     ImTextureID = GLuint                       (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
558          DirectX9:   ImTextureID = LPDIRECT3DTEXTURE9           (see ImGui_ImplDX9_RenderDrawData()     function in imgui_impl_dx9.cpp)
559          DirectX11:  ImTextureID = ID3D11ShaderResourceView*    (see ImGui_ImplDX11_RenderDrawData()    function in imgui_impl_dx11.cpp)
560          DirectX12:  ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE  (see ImGui_ImplDX12_RenderDrawData()    function in imgui_impl_dx12.cpp)
561 
562       For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
563       Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
564       tying together both the texture and information about its format and how to read it.
565     - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about
566       the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase
567       is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
568       If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID
569       representation suggested by the example bindings is probably the best choice.
570       (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
571 
572     User code may do:
573 
574         // Cast our texture type to ImTextureID / void*
575         MyTexture* texture = g_CoffeeTableTexture;
576         ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
577 
578     The renderer function called after ImGui::Render() will receive that same value that the user code passed:
579 
580         // Cast ImTextureID / void* stored in the draw command as our texture type
581         MyTexture* texture = (MyTexture*)pcmd->TextureId;
582         MyEngineBindTexture2D(texture);
583 
584     Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
585     This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them.
586     If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using.
587 
588     Here's a simplified OpenGL example using stb_image.h:
589 
590         // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
591         #define STB_IMAGE_IMPLEMENTATION
592         #include <stb_image.h>
593         [...]
594         int my_image_width, my_image_height;
595         unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
596 
597         // Turn the RGBA pixel data into an OpenGL texture:
598         GLuint my_opengl_texture;
599         glGenTextures(1, &my_opengl_texture);
600         glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
601         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
602         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
603         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
604         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
605 
606         // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it:
607         ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
608 
609     C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa.
610     Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
611     Examples:
612 
613         GLuint my_tex = XXX;
614         void* my_void_ptr;
615         my_void_ptr = (void*)(intptr_t)my_tex;                  // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer)
616         my_tex = (GLuint)(intptr_t)my_void_ptr;                 // cast a void* into a GLuint
617 
618         ID3D11ShaderResourceView* my_dx11_srv = XXX;
619         void* my_void_ptr;
620         my_void_ptr = (void*)my_dx11_srv;                       // cast a ID3D11ShaderResourceView* into an opaque void*
621         my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr;   // cast a void* into a ID3D11ShaderResourceView*
622 
623     Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
624 
625  Q: How can I have multiple widgets with the same label or without a label?
626  Q: I have multiple widgets with the same label, and only the first one works. Why is that?
627  A: A primer on labels and the ID Stack...
628 
629     Dear ImGui internally need to uniquely identify UI elements.
630     Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
631     Interactive widgets (such as calls to Button buttons) need a unique ID.
632     Unique ID are used internally to track active widgets and occasionally associate state to widgets.
633     Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
634 
635    - Unique ID are often derived from a string label:
636 
637        Button("OK");          // Label = "OK",     ID = hash of (..., "OK")
638        Button("Cancel");      // Label = "Cancel", ID = hash of (..., "Cancel")
639 
640    - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
641      two buttons labeled "OK" in different windows or different tree locations is fine.
642      We used "..." above to signify whatever was already pushed to the ID stack previously:
643 
644        Begin("MyWindow");
645        Button("OK");          // Label = "OK",     ID = hash of ("MyWindow", "OK")
646        End();
647 
648    - If you have a same ID twice in the same location, you'll have a conflict:
649 
650        Button("OK");
651        Button("OK");          // ID collision! Interacting with either button will trigger the first one.
652 
653      Fear not! this is easy to solve and there are many ways to solve it!
654 
655    - Solving ID conflict in a simple/local context:
656      When passing a label you can optionally specify extra ID information within string itself.
657      Use "##" to pass a complement to the ID that won't be visible to the end-user.
658      This helps solving the simple collision cases when you know e.g. at compilation time which items
659      are going to be created:
660 
661        Begin("MyWindow");
662        Button("Play");        // Label = "Play",   ID = hash of ("MyWindow", "Play")
663        Button("Play##foo1");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo1")  // Different from above
664        Button("Play##foo2");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo2")  // Different from above
665        End();
666 
667    - If you want to completely hide the label, but still need an ID:
668 
669        Checkbox("##On", &b);  // Label = "",       ID = hash of (..., "##On")   // No visible label, just a checkbox!
670 
671    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
672      you to animate labels. For example you may want to include varying information in a window title bar,
673      but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
674 
675        Button("Hello###ID");  // Label = "Hello",  ID = hash of (..., "ID")
676        Button("World###ID");  // Label = "World",  ID = hash of (..., "ID")     // Same as above, even though the label looks different
677 
678        sprintf(buf, "My game (%f FPS)###MyGame", fps);
679        Begin(buf);            // Variable title,   ID = hash of "MyGame"
680 
681    - Solving ID conflict in a more general manner:
682      Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
683      within the same window. This is the most convenient way of distinguishing ID when iterating and
684      creating many UI elements programmatically.
685      You can push a pointer, a string or an integer value into the ID stack.
686      Remember that ID are formed from the concatenation of _everything_ in the ID stack!
687 
688        Begin("Window");
689        for (int i = 0; i < 100; i++)
690        {
691          PushID(i);         // Push i to the id tack
692          Button("Click");   // Label = "Click",  ID = Hash of ("Window", i, "Click")
693          PopID();
694        }
695        for (int i = 0; i < 100; i++)
696        {
697          MyObject* obj = Objects[i];
698          PushID(obj);
699          Button("Click");   // Label = "Click",  ID = Hash of ("Window", obj pointer, "Click")
700          PopID();
701        }
702        for (int i = 0; i < 100; i++)
703        {
704          MyObject* obj = Objects[i];
705          PushID(obj->Name);
706          Button("Click");   // Label = "Click",  ID = Hash of ("Window", obj->Name, "Click")
707          PopID();
708        }
709        End();
710 
711    - More example showing that you can stack multiple prefixes into the ID stack:
712 
713        Button("Click");     // Label = "Click",  ID = hash of (..., "Click")
714        PushID("node");
715        Button("Click");     // Label = "Click",  ID = hash of (..., "node", "Click")
716          PushID(my_ptr);
717            Button("Click"); // Label = "Click",  ID = hash of (..., "node", my_ptr, "Click")
718          PopID();
719        PopID();
720 
721    - Tree nodes implicitly creates a scope for you by calling PushID().
722 
723        Button("Click");     // Label = "Click",  ID = hash of (..., "Click")
724        if (TreeNode("node"))
725        {
726          Button("Click");   // Label = "Click",  ID = hash of (..., "node", "Click")
727          TreePop();
728        }
729 
730    - When working with trees, ID are used to preserve the open/close state of each tree node.
731      Depending on your use cases you may want to use strings, indices or pointers as ID.
732       e.g. when following a single pointer that may change over time, using a static string as ID
733        will preserve your node open/closed state when the targeted object change.
734       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
735        node open/closed state differently. See what makes more sense in your situation!
736 
737  Q: How can I use my own math types instead of ImVec2/ImVec4?
738  A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
739     This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
740 
741  Q: How can I load a different font than the default?
742  A: Use the font atlas to load the TTF/OTF file you want:
743       ImGuiIO& io = ImGui::GetIO();
744       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
745       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
746     Default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code.
747     (Read the 'misc/fonts/README.txt' file for more details about font loading.)
748 
749     New programmers: remember that in C/C++ and most programming languages if you want to use a
750     backslash \ within a string literal, you need to write it double backslash "\\":
751       io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels);   // WRONG (you are escape the M here!)
752       io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels);  // CORRECT
753       io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels);   // ALSO CORRECT
754 
755  Q: How can I easily use icons in my application?
756  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
757     main font. Then you can refer to icons within your strings.
758     (Read the 'misc/fonts/README.txt' file for more details about icons font loading.)
759 
760  Q: How can I load multiple fonts?
761  A: Use the font atlas to pack them into a single texture:
762     (Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.)
763 
764       ImGuiIO& io = ImGui::GetIO();
765       ImFont* font0 = io.Fonts->AddFontDefault();
766       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
767       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
768       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
769       // the first loaded font gets used by default
770       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
771 
772       // Options
773       ImFontConfig config;
774       config.OversampleH = 3;
775       config.OversampleV = 1;
776       config.GlyphOffset.y -= 2.0f;      // Move everything by 2 pixels up
777       config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
778       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config);
779 
780       // Combine multiple fonts into one (e.g. for icon fonts)
781       static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
782       ImFontConfig config;
783       config.MergeMode = true;
784       io.Fonts->AddFontDefault();
785       io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
786       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
787 
788  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
789  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
790 
791       // Add default Japanese ranges
792       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
793 
794       // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
795       ImVector<ImWchar> ranges;
796       ImFontAtlas::GlyphRangesBuilder builder;
797       builder.AddText("Hello world");                        // Add a string (here "Hello world" contains 7 unique characters)
798       builder.AddChar(0x7262);                               // Add a specific character
799       builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
800       builder.BuildRanges(&ranges);                          // Build the final result (ordered ranges with all the unique characters submitted)
801       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
802 
803     All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
804     by using the u8"hello" syntax. Specifying literal in your source code using a local code page
805     (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
806     Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
807 
808     Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
809     The applications in examples/ are doing that.
810     Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
811     You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
812     Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for
813     the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
814 
815  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
816  A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags.
817       (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse)
818       Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
819     - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
820     - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData,
821       and then call your rendered code with your own ImDrawList or ImDrawData data.
822 
823  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
824  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
825     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
826 
827  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
828  A: You are probably mishandling the clipping rectangles in your render function.
829     Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
830 
831  Q: How can I help?
832  A: - If you are experienced with Dear ImGui and C++, look at the github issues, or docs/TODO.txt and see how you want/can help!
833     - Convince your company to sponsor/fund development! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README.
834     - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
835       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers.
836       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
837     - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately).
838 
839  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
840         this is also useful to set yourself in the context of another window (to get/set other settings)
841  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
842  - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
843         of a deep nested inner loop in your code.
844  - tip: you can call Render() multiple times (e.g for VR renders).
845  - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
846 
847 */
848 
849 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
850 #define _CRT_SECURE_NO_WARNINGS
851 #endif
852 
853 #include "imgui.h"
854 #ifndef IMGUI_DEFINE_MATH_OPERATORS
855 #define IMGUI_DEFINE_MATH_OPERATORS
856 #endif
857 #include "imgui_internal.h"
858 
859 #include <ctype.h>      // toupper, isprint
860 #include <stdio.h>      // vsnprintf, sscanf, printf
861 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
862 #include <stddef.h>     // intptr_t
863 #else
864 #include <stdint.h>     // intptr_t
865 #endif
866 
867 // Debug options
868 #define IMGUI_DEBUG_NAV_SCORING     0
869 #define IMGUI_DEBUG_NAV_RECTS       0
870 
871 // Visual Studio warnings
872 #ifdef _MSC_VER
873 #pragma warning (disable: 4127)     // condition expression is constant
874 #pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
875 #endif
876 
877 // Clang/GCC warnings with -Weverything
878 #ifdef __clang__
879 #pragma clang diagnostic ignored "-Wunknown-pragmas"        // warning : unknown warning group '-Wformat-pedantic *'        // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
880 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
881 #pragma clang diagnostic ignored "-Wfloat-equal"            // warning : comparing floating point with == or != is unsafe   // storing and comparing against same constants (typically 0.0f) is ok.
882 #pragma clang diagnostic ignored "-Wformat-nonliteral"      // warning : format string is not a string literal              // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
883 #pragma clang diagnostic ignored "-Wexit-time-destructors"  // warning : declaration requires an exit-time destructor       // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
884 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
885 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
886 #pragma clang diagnostic ignored "-Wformat-pedantic"        // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
887 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
888 #elif defined(__GNUC__)
889 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
890 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
891 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
892 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
893 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
894 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
895 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
896 #if __GNUC__ >= 8
897 #pragma GCC diagnostic ignored "-Wclass-memaccess"          // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
898 #endif
899 #endif
900 
901 // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.
902 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
903 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
904 
905 // Window resizing from edges (when io.ConfigResizeWindowsFromEdges = true)
906 static const float RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
907 static const float RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
908 
909 //-------------------------------------------------------------------------
910 // [SECTION] FORWARD DECLARATIONS
911 //-------------------------------------------------------------------------
912 
913 static void             SetCurrentWindow(ImGuiWindow* window);
914 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
915 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
916 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
917 static void             FindHoveredWindow();
918 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
919 static void             CheckStacksSize(ImGuiWindow* window, bool write);
920 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
921 
922 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
923 static void             AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
924 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
925 
926 static ImRect           GetViewportRect();
927 
928 // Settings
929 static void*            SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
930 static void             SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
931 static void             SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
932 
933 // Platform Dependents default implementation for IO functions
934 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
935 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
936 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
937 
938 namespace ImGui
939 {
940 static bool             BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
941 
942 // Navigation
943 static void             NavUpdate();
944 static void             NavUpdateWindowing();
945 static void             NavUpdateWindowingList();
946 static void             NavUpdateMoveResult();
947 static float            NavUpdatePageUpPageDown(int allowed_dir_flags);
948 static inline void      NavUpdateAnyRequestFlag();
949 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
950 static ImVec2           NavCalcPreferredRefPos();
951 static void             NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
952 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
953 
954 // Misc
955 static void             UpdateMouseInputs();
956 static void             UpdateMouseWheel();
957 static void             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
958 }
959 
960 // Test engine hooks (imgui-test)
961 //#define IMGUI_ENABLE_TEST_ENGINE_HOOKS
962 #ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
963 extern void             ImGuiTestEngineHook_PreNewFrame();
964 extern void             ImGuiTestEngineHook_PostNewFrame();
965 extern void             ImGuiTestEngineHook_ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg);
966 #endif
967 
968 //-----------------------------------------------------------------------------
969 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
970 //-----------------------------------------------------------------------------
971 
972 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
973 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
974 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
975 // ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can:
976 // - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
977 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
978 #ifndef GImGui
979 ImGuiContext*   GImGui = NULL;
980 #endif
981 
982 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
983 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
984 // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
985 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)986 static void*   MallocWrapper(size_t size, void* user_data)    { (void)user_data; return malloc(size); }
FreeWrapper(void * ptr,void * user_data)987 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; free(ptr); }
988 #else
MallocWrapper(size_t size,void * user_data)989 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)990 static void    FreeWrapper(void* ptr, void* user_data)        { (void)user_data; (void)ptr; IM_ASSERT(0); }
991 #endif
992 
993 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
994 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
995 static void*    GImAllocatorUserData = NULL;
996 
997 //-----------------------------------------------------------------------------
998 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
999 //-----------------------------------------------------------------------------
1000 
ImGuiStyle()1001 ImGuiStyle::ImGuiStyle()
1002 {
1003     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
1004     WindowPadding           = ImVec2(8,8);      // Padding within a window
1005     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
1006     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1007     WindowMinSize           = ImVec2(32,32);    // Minimum window size
1008     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
1009     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1010     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1011     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1012     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1013     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
1014     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1015     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1016     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
1017     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1018     TouchExtraPadding       = ImVec2(0,0);      // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
1019     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1020     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
1021     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
1022     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
1023     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
1024     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1025     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1026     DisplayWindowPadding    = ImVec2(20,20);    // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
1027     DisplaySafeAreaPadding  = ImVec2(3,3);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
1028     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1029     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1030     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1031     CurveTessellationTol    = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
1032 
1033     // Default theme
1034     ImGui::StyleColorsDark(this);
1035 }
1036 
1037 // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
1038 // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
ScaleAllSizes(float scale_factor)1039 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1040 {
1041     WindowPadding = ImFloor(WindowPadding * scale_factor);
1042     WindowRounding = ImFloor(WindowRounding * scale_factor);
1043     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1044     ChildRounding = ImFloor(ChildRounding * scale_factor);
1045     PopupRounding = ImFloor(PopupRounding * scale_factor);
1046     FramePadding = ImFloor(FramePadding * scale_factor);
1047     FrameRounding = ImFloor(FrameRounding * scale_factor);
1048     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1049     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1050     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1051     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1052     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1053     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1054     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1055     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1056     GrabRounding = ImFloor(GrabRounding * scale_factor);
1057     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1058     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1059     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1060 }
1061 
ImGuiIO()1062 ImGuiIO::ImGuiIO()
1063 {
1064     // Most fields are initialized with zero
1065     memset(this, 0, sizeof(*this));
1066 
1067     // Settings
1068     ConfigFlags = ImGuiConfigFlags_None;
1069     BackendFlags = ImGuiBackendFlags_None;
1070     DisplaySize = ImVec2(-1.0f, -1.0f);
1071     DeltaTime = 1.0f/60.0f;
1072     IniSavingRate = 5.0f;
1073     IniFilename = "imgui.ini";
1074     LogFilename = "imgui_log.txt";
1075     MouseDoubleClickTime = 0.30f;
1076     MouseDoubleClickMaxDist = 6.0f;
1077     for (int i = 0; i < ImGuiKey_COUNT; i++)
1078         KeyMap[i] = -1;
1079     KeyRepeatDelay = 0.250f;
1080     KeyRepeatRate = 0.050f;
1081     UserData = NULL;
1082 
1083     Fonts = NULL;
1084     FontGlobalScale = 1.0f;
1085     FontDefault = NULL;
1086     FontAllowUserScaling = false;
1087     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1088     DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
1089 
1090     // Miscellaneous configuration options
1091 #ifdef __APPLE__
1092     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1093 #else
1094     ConfigMacOSXBehaviors = false;
1095 #endif
1096     ConfigInputTextCursorBlink = true;
1097     ConfigResizeWindowsFromEdges = false;
1098 
1099     // Platform Functions
1100     BackendPlatformName = BackendRendererName = NULL;
1101     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1102     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1103     ClipboardUserData = NULL;
1104     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1105     ImeWindowHandle = NULL;
1106 
1107 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1108     RenderDrawListsFn = NULL;
1109 #endif
1110 
1111     // Input (NB: we already have memset zero the entire structure!)
1112     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1113     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1114     MouseDragThreshold = 6.0f;
1115     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1116     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1117     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1118 }
1119 
1120 // Pass in translated ASCII characters for text input.
1121 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1122 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)1123 void ImGuiIO::AddInputCharacter(ImWchar c)
1124 {
1125     const int n = ImStrlenW(InputCharacters);
1126     if (n + 1 < IM_ARRAYSIZE(InputCharacters))
1127     {
1128         InputCharacters[n] = c;
1129         InputCharacters[n+1] = '\0';
1130     }
1131 }
1132 
AddInputCharactersUTF8(const char * utf8_chars)1133 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1134 {
1135     // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
1136     const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
1137     ImWchar wchars[wchars_buf_len];
1138     ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
1139     for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
1140         AddInputCharacter(wchars[i]);
1141 }
1142 
1143 //-----------------------------------------------------------------------------
1144 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1145 //-----------------------------------------------------------------------------
1146 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1147 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1148 {
1149     ImVec2 ap = p - a;
1150     ImVec2 ab_dir = b - a;
1151     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1152     if (dot < 0.0f)
1153         return a;
1154     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1155     if (dot > ab_len_sqr)
1156         return b;
1157     return a + ab_dir * dot / ab_len_sqr;
1158 }
1159 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1160 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1161 {
1162     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1163     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1164     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1165     return ((b1 == b2) && (b2 == b3));
1166 }
1167 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1168 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1169 {
1170     ImVec2 v0 = b - a;
1171     ImVec2 v1 = c - a;
1172     ImVec2 v2 = p - a;
1173     const float denom = v0.x * v1.y - v1.x * v0.y;
1174     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1175     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1176     out_u = 1.0f - out_v - out_w;
1177 }
1178 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1179 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1180 {
1181     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1182     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1183     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1184     float dist2_ab = ImLengthSqr(p - proj_ab);
1185     float dist2_bc = ImLengthSqr(p - proj_bc);
1186     float dist2_ca = ImLengthSqr(p - proj_ca);
1187     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1188     if (m == dist2_ab)
1189         return proj_ab;
1190     if (m == dist2_bc)
1191         return proj_bc;
1192     return proj_ca;
1193 }
1194 
ImStricmp(const char * str1,const char * str2)1195 int ImStricmp(const char* str1, const char* str2)
1196 {
1197     int d;
1198     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1199     return d;
1200 }
1201 
ImStrnicmp(const char * str1,const char * str2,size_t count)1202 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1203 {
1204     int d = 0;
1205     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1206     return d;
1207 }
1208 
ImStrncpy(char * dst,const char * src,size_t count)1209 void ImStrncpy(char* dst, const char* src, size_t count)
1210 {
1211     if (count < 1) return;
1212     strncpy(dst, src, count);
1213     dst[count-1] = 0;
1214 }
1215 
ImStrdup(const char * str)1216 char* ImStrdup(const char *str)
1217 {
1218     size_t len = strlen(str) + 1;
1219     void* buf = ImGui::MemAlloc(len);
1220     return (char*)memcpy(buf, (const void*)str, len);
1221 }
1222 
ImStrchrRange(const char * str,const char * str_end,char c)1223 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1224 {
1225     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1226     return p;
1227 }
1228 
ImStrlenW(const ImWchar * str)1229 int ImStrlenW(const ImWchar* str)
1230 {
1231     //return (int)wcslen((const wchar_t*)str);	// FIXME-OPT: Could use this when wchar_t are 16-bits
1232     int n = 0;
1233     while (*str++) n++;
1234     return n;
1235 }
1236 
1237 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1238 const char* ImStreolRange(const char* str, const char* str_end)
1239 {
1240     const char* p = (const char*)memchr(str, '\n', str_end - str);
1241     return p ? p : str_end;
1242 }
1243 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1244 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1245 {
1246     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1247         buf_mid_line--;
1248     return buf_mid_line;
1249 }
1250 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1251 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1252 {
1253     if (!needle_end)
1254         needle_end = needle + strlen(needle);
1255 
1256     const char un0 = (char)toupper(*needle);
1257     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1258     {
1259         if (toupper(*haystack) == un0)
1260         {
1261             const char* b = needle + 1;
1262             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1263                 if (toupper(*a) != toupper(*b))
1264                     break;
1265             if (b == needle_end)
1266                 return haystack;
1267         }
1268         haystack++;
1269     }
1270     return NULL;
1271 }
1272 
1273 // Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
ImStrTrimBlanks(char * buf)1274 void ImStrTrimBlanks(char* buf)
1275 {
1276     char* p = buf;
1277     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1278         p++;
1279     char* p_start = p;
1280     while (*p != 0)                         // Find end of string
1281         p++;
1282     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1283         p--;
1284     if (p_start != buf)                     // Copy memory if we had leading blanks
1285         memmove(buf, p_start, p - p_start);
1286     buf[p - p_start] = 0;                   // Zero terminate
1287 }
1288 
1289 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1290 // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
1291 // B) When buf==NULL vsnprintf() will return the output size.
1292 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1293 
1294 #if defined(_MSC_VER) && !defined(vsnprintf)
1295 #define vsnprintf _vsnprintf
1296 #endif
1297 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1298 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1299 {
1300     va_list args;
1301     va_start(args, fmt);
1302     int w = vsnprintf(buf, buf_size, fmt, args);
1303     va_end(args);
1304     if (buf == NULL)
1305         return w;
1306     if (w == -1 || w >= (int)buf_size)
1307         w = (int)buf_size - 1;
1308     buf[w] = 0;
1309     return w;
1310 }
1311 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1312 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1313 {
1314     int w = vsnprintf(buf, buf_size, fmt, args);
1315     if (buf == NULL)
1316         return w;
1317     if (w == -1 || w >= (int)buf_size)
1318         w = (int)buf_size - 1;
1319     buf[w] = 0;
1320     return w;
1321 }
1322 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1323 
1324 // Pass data_size==0 for zero-terminated strings
1325 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHash(const void * data,int data_size,ImU32 seed)1326 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1327 {
1328     static ImU32 crc32_lut[256] = { 0 };
1329     if (!crc32_lut[1])
1330     {
1331         const ImU32 polynomial = 0xEDB88320;
1332         for (ImU32 i = 0; i < 256; i++)
1333         {
1334             ImU32 crc = i;
1335             for (ImU32 j = 0; j < 8; j++)
1336                 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1337             crc32_lut[i] = crc;
1338         }
1339     }
1340 
1341     seed = ~seed;
1342     ImU32 crc = seed;
1343     const unsigned char* current = (const unsigned char*)data;
1344 
1345     if (data_size > 0)
1346     {
1347         // Known size
1348         while (data_size--)
1349             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1350     }
1351     else
1352     {
1353         // Zero-terminated string
1354         while (unsigned char c = *current++)
1355         {
1356             // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1357             // Because this syntax is rarely used we are optimizing for the common case.
1358             // - If we reach ### in the string we discard the hash so far and reset to the seed.
1359             // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1360             if (c == '#' && current[0] == '#' && current[1] == '#')
1361                 crc = seed;
1362             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1363         }
1364     }
1365     return ~crc;
1366 }
1367 
ImFileOpen(const char * filename,const char * mode)1368 FILE* ImFileOpen(const char* filename, const char* mode)
1369 {
1370 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1371     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1372     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1373     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1374     ImVector<ImWchar> buf;
1375     buf.resize(filename_wsize + mode_wsize);
1376     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1377     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1378     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1379 #else
1380     return fopen(filename, mode);
1381 #endif
1382 }
1383 
1384 // Load file content into memory
1385 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,size_t * out_file_size,int padding_bytes)1386 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1387 {
1388     IM_ASSERT(filename && file_open_mode);
1389     if (out_file_size)
1390         *out_file_size = 0;
1391 
1392     FILE* f;
1393     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1394         return NULL;
1395 
1396     long file_size_signed;
1397     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1398     {
1399         fclose(f);
1400         return NULL;
1401     }
1402 
1403     size_t file_size = (size_t)file_size_signed;
1404     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1405     if (file_data == NULL)
1406     {
1407         fclose(f);
1408         return NULL;
1409     }
1410     if (fread(file_data, 1, file_size, f) != file_size)
1411     {
1412         fclose(f);
1413         ImGui::MemFree(file_data);
1414         return NULL;
1415     }
1416     if (padding_bytes > 0)
1417         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1418 
1419     fclose(f);
1420     if (out_file_size)
1421         *out_file_size = file_size;
1422 
1423     return file_data;
1424 }
1425 
1426 //-----------------------------------------------------------------------------
1427 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1428 //-----------------------------------------------------------------------------
1429 
1430 // Convert UTF-8 to 32-bits character, process single character input.
1431 // Based on stb_from_utf8() from github.com/nothings/stb/
1432 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1433 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1434 {
1435     unsigned int c = (unsigned int)-1;
1436     const unsigned char* str = (const unsigned char*)in_text;
1437     if (!(*str & 0x80))
1438     {
1439         c = (unsigned int)(*str++);
1440         *out_char = c;
1441         return 1;
1442     }
1443     if ((*str & 0xe0) == 0xc0)
1444     {
1445         *out_char = 0xFFFD; // will be invalid but not end of string
1446         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1447         if (*str < 0xc2) return 2;
1448         c = (unsigned int)((*str++ & 0x1f) << 6);
1449         if ((*str & 0xc0) != 0x80) return 2;
1450         c += (*str++ & 0x3f);
1451         *out_char = c;
1452         return 2;
1453     }
1454     if ((*str & 0xf0) == 0xe0)
1455     {
1456         *out_char = 0xFFFD; // will be invalid but not end of string
1457         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1458         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1459         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1460         c = (unsigned int)((*str++ & 0x0f) << 12);
1461         if ((*str & 0xc0) != 0x80) return 3;
1462         c += (unsigned int)((*str++ & 0x3f) << 6);
1463         if ((*str & 0xc0) != 0x80) return 3;
1464         c += (*str++ & 0x3f);
1465         *out_char = c;
1466         return 3;
1467     }
1468     if ((*str & 0xf8) == 0xf0)
1469     {
1470         *out_char = 0xFFFD; // will be invalid but not end of string
1471         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1472         if (*str > 0xf4) return 4;
1473         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1474         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1475         c = (unsigned int)((*str++ & 0x07) << 18);
1476         if ((*str & 0xc0) != 0x80) return 4;
1477         c += (unsigned int)((*str++ & 0x3f) << 12);
1478         if ((*str & 0xc0) != 0x80) return 4;
1479         c += (unsigned int)((*str++ & 0x3f) << 6);
1480         if ((*str & 0xc0) != 0x80) return 4;
1481         c += (*str++ & 0x3f);
1482         // utf-8 encodings of values used in surrogate pairs are invalid
1483         if ((c & 0xFFFFF800) == 0xD800) return 4;
1484         *out_char = c;
1485         return 4;
1486     }
1487     *out_char = 0;
1488     return 0;
1489 }
1490 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1491 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1492 {
1493     ImWchar* buf_out = buf;
1494     ImWchar* buf_end = buf + buf_size;
1495     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1496     {
1497         unsigned int c;
1498         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1499         if (c == 0)
1500             break;
1501         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1502             *buf_out++ = (ImWchar)c;
1503     }
1504     *buf_out = 0;
1505     if (in_text_remaining)
1506         *in_text_remaining = in_text;
1507     return (int)(buf_out - buf);
1508 }
1509 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1510 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1511 {
1512     int char_count = 0;
1513     while ((!in_text_end || in_text < in_text_end) && *in_text)
1514     {
1515         unsigned int c;
1516         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1517         if (c == 0)
1518             break;
1519         if (c < 0x10000)
1520             char_count++;
1521     }
1522     return char_count;
1523 }
1524 
1525 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1526 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1527 {
1528     if (c < 0x80)
1529     {
1530         buf[0] = (char)c;
1531         return 1;
1532     }
1533     if (c < 0x800)
1534     {
1535         if (buf_size < 2) return 0;
1536         buf[0] = (char)(0xc0 + (c >> 6));
1537         buf[1] = (char)(0x80 + (c & 0x3f));
1538         return 2;
1539     }
1540     if (c >= 0xdc00 && c < 0xe000)
1541     {
1542         return 0;
1543     }
1544     if (c >= 0xd800 && c < 0xdc00)
1545     {
1546         if (buf_size < 4) return 0;
1547         buf[0] = (char)(0xf0 + (c >> 18));
1548         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1549         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1550         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1551         return 4;
1552     }
1553     //else if (c < 0x10000)
1554     {
1555         if (buf_size < 3) return 0;
1556         buf[0] = (char)(0xe0 + (c >> 12));
1557         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1558         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1559         return 3;
1560     }
1561 }
1562 
1563 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1564 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1565 {
1566     unsigned int dummy = 0;
1567     return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1568 }
1569 
ImTextCountUtf8BytesFromChar(unsigned int c)1570 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1571 {
1572     if (c < 0x80) return 1;
1573     if (c < 0x800) return 2;
1574     if (c >= 0xdc00 && c < 0xe000) return 0;
1575     if (c >= 0xd800 && c < 0xdc00) return 4;
1576     return 3;
1577 }
1578 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1579 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1580 {
1581     char* buf_out = buf;
1582     const char* buf_end = buf + buf_size;
1583     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1584     {
1585         unsigned int c = (unsigned int)(*in_text++);
1586         if (c < 0x80)
1587             *buf_out++ = (char)c;
1588         else
1589             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1590     }
1591     *buf_out = 0;
1592     return (int)(buf_out - buf);
1593 }
1594 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1595 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1596 {
1597     int bytes_count = 0;
1598     while ((!in_text_end || in_text < in_text_end) && *in_text)
1599     {
1600         unsigned int c = (unsigned int)(*in_text++);
1601         if (c < 0x80)
1602             bytes_count++;
1603         else
1604             bytes_count += ImTextCountUtf8BytesFromChar(c);
1605     }
1606     return bytes_count;
1607 }
1608 
1609 //-----------------------------------------------------------------------------
1610 // [SECTION] MISC HELPER/UTILTIES (Color functions)
1611 // Note: The Convert functions are early design which are not consistent with other API.
1612 //-----------------------------------------------------------------------------
1613 
ColorConvertU32ToFloat4(ImU32 in)1614 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1615 {
1616     float s = 1.0f/255.0f;
1617     return ImVec4(
1618         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1619         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1620         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1621         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1622 }
1623 
ColorConvertFloat4ToU32(const ImVec4 & in)1624 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1625 {
1626     ImU32 out;
1627     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1628     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1629     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1630     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1631     return out;
1632 }
1633 
1634 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1635 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
ColorConvertRGBtoHSV(float r,float g,float b,float & out_h,float & out_s,float & out_v)1636 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1637 {
1638     float K = 0.f;
1639     if (g < b)
1640     {
1641         ImSwap(g, b);
1642         K = -1.f;
1643     }
1644     if (r < g)
1645     {
1646         ImSwap(r, g);
1647         K = -2.f / 6.f - K;
1648     }
1649 
1650     const float chroma = r - (g < b ? g : b);
1651     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1652     out_s = chroma / (r + 1e-20f);
1653     out_v = r;
1654 }
1655 
1656 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1657 // also http://en.wikipedia.org/wiki/HSL_and_HSV
ColorConvertHSVtoRGB(float h,float s,float v,float & out_r,float & out_g,float & out_b)1658 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1659 {
1660     if (s == 0.0f)
1661     {
1662         // gray
1663         out_r = out_g = out_b = v;
1664         return;
1665     }
1666 
1667     h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1668     int   i = (int)h;
1669     float f = h - (float)i;
1670     float p = v * (1.0f - s);
1671     float q = v * (1.0f - s * f);
1672     float t = v * (1.0f - s * (1.0f - f));
1673 
1674     switch (i)
1675     {
1676     case 0: out_r = v; out_g = t; out_b = p; break;
1677     case 1: out_r = q; out_g = v; out_b = p; break;
1678     case 2: out_r = p; out_g = v; out_b = t; break;
1679     case 3: out_r = p; out_g = q; out_b = v; break;
1680     case 4: out_r = t; out_g = p; out_b = v; break;
1681     case 5: default: out_r = v; out_g = p; out_b = q; break;
1682     }
1683 }
1684 
GetColorU32(ImGuiCol idx,float alpha_mul)1685 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1686 {
1687     ImGuiStyle& style = GImGui->Style;
1688     ImVec4 c = style.Colors[idx];
1689     c.w *= style.Alpha * alpha_mul;
1690     return ColorConvertFloat4ToU32(c);
1691 }
1692 
GetColorU32(const ImVec4 & col)1693 ImU32 ImGui::GetColorU32(const ImVec4& col)
1694 {
1695     ImGuiStyle& style = GImGui->Style;
1696     ImVec4 c = col;
1697     c.w *= style.Alpha;
1698     return ColorConvertFloat4ToU32(c);
1699 }
1700 
GetStyleColorVec4(ImGuiCol idx)1701 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1702 {
1703     ImGuiStyle& style = GImGui->Style;
1704     return style.Colors[idx];
1705 }
1706 
GetColorU32(ImU32 col)1707 ImU32 ImGui::GetColorU32(ImU32 col)
1708 {
1709     float style_alpha = GImGui->Style.Alpha;
1710     if (style_alpha >= 1.0f)
1711         return col;
1712     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1713     a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1714     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1715 }
1716 
1717 //-----------------------------------------------------------------------------
1718 // [SECTION] ImGuiStorage
1719 // Helper: Key->value storage
1720 //-----------------------------------------------------------------------------
1721 
1722 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1723 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1724 {
1725     ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1726     ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1727     size_t count = (size_t)(last - first);
1728     while (count > 0)
1729     {
1730         size_t count2 = count >> 1;
1731         ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1732         if (mid->key < key)
1733         {
1734             first = ++mid;
1735             count -= count2 + 1;
1736         }
1737         else
1738         {
1739             count = count2;
1740         }
1741     }
1742     return first;
1743 }
1744 
1745 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1746 void ImGuiStorage::BuildSortByKey()
1747 {
1748     struct StaticFunc
1749     {
1750         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1751         {
1752             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1753             if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1754             if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1755             return 0;
1756         }
1757     };
1758     if (Data.Size > 1)
1759         ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1760 }
1761 
GetInt(ImGuiID key,int default_val) const1762 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1763 {
1764     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1765     if (it == Data.end() || it->key != key)
1766         return default_val;
1767     return it->val_i;
1768 }
1769 
GetBool(ImGuiID key,bool default_val) const1770 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1771 {
1772     return GetInt(key, default_val ? 1 : 0) != 0;
1773 }
1774 
GetFloat(ImGuiID key,float default_val) const1775 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1776 {
1777     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1778     if (it == Data.end() || it->key != key)
1779         return default_val;
1780     return it->val_f;
1781 }
1782 
GetVoidPtr(ImGuiID key) const1783 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1784 {
1785     ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1786     if (it == Data.end() || it->key != key)
1787         return NULL;
1788     return it->val_p;
1789 }
1790 
1791 // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
GetIntRef(ImGuiID key,int default_val)1792 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1793 {
1794     ImVector<Pair>::iterator it = LowerBound(Data, key);
1795     if (it == Data.end() || it->key != key)
1796         it = Data.insert(it, Pair(key, default_val));
1797     return &it->val_i;
1798 }
1799 
GetBoolRef(ImGuiID key,bool default_val)1800 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1801 {
1802     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1803 }
1804 
GetFloatRef(ImGuiID key,float default_val)1805 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1806 {
1807     ImVector<Pair>::iterator it = LowerBound(Data, key);
1808     if (it == Data.end() || it->key != key)
1809         it = Data.insert(it, Pair(key, default_val));
1810     return &it->val_f;
1811 }
1812 
GetVoidPtrRef(ImGuiID key,void * default_val)1813 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1814 {
1815     ImVector<Pair>::iterator it = LowerBound(Data, key);
1816     if (it == Data.end() || it->key != key)
1817         it = Data.insert(it, Pair(key, default_val));
1818     return &it->val_p;
1819 }
1820 
1821 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
SetInt(ImGuiID key,int val)1822 void ImGuiStorage::SetInt(ImGuiID key, int val)
1823 {
1824     ImVector<Pair>::iterator it = LowerBound(Data, key);
1825     if (it == Data.end() || it->key != key)
1826     {
1827         Data.insert(it, Pair(key, val));
1828         return;
1829     }
1830     it->val_i = val;
1831 }
1832 
SetBool(ImGuiID key,bool val)1833 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1834 {
1835     SetInt(key, val ? 1 : 0);
1836 }
1837 
SetFloat(ImGuiID key,float val)1838 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1839 {
1840     ImVector<Pair>::iterator it = LowerBound(Data, key);
1841     if (it == Data.end() || it->key != key)
1842     {
1843         Data.insert(it, Pair(key, val));
1844         return;
1845     }
1846     it->val_f = val;
1847 }
1848 
SetVoidPtr(ImGuiID key,void * val)1849 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1850 {
1851     ImVector<Pair>::iterator it = LowerBound(Data, key);
1852     if (it == Data.end() || it->key != key)
1853     {
1854         Data.insert(it, Pair(key, val));
1855         return;
1856     }
1857     it->val_p = val;
1858 }
1859 
SetAllInt(int v)1860 void ImGuiStorage::SetAllInt(int v)
1861 {
1862     for (int i = 0; i < Data.Size; i++)
1863         Data[i].val_i = v;
1864 }
1865 
1866 //-----------------------------------------------------------------------------
1867 // [SECTION] ImGuiTextFilter
1868 //-----------------------------------------------------------------------------
1869 
1870 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1871 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1872 {
1873     if (default_filter)
1874     {
1875         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1876         Build();
1877     }
1878     else
1879     {
1880         InputBuf[0] = 0;
1881         CountGrep = 0;
1882     }
1883 }
1884 
Draw(const char * label,float width)1885 bool ImGuiTextFilter::Draw(const char* label, float width)
1886 {
1887     if (width != 0.0f)
1888         ImGui::PushItemWidth(width);
1889     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1890     if (width != 0.0f)
1891         ImGui::PopItemWidth();
1892     if (value_changed)
1893         Build();
1894     return value_changed;
1895 }
1896 
split(char separator,ImVector<TextRange> * out) const1897 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
1898 {
1899     out->resize(0);
1900     const char* wb = b;
1901     const char* we = wb;
1902     while (we < e)
1903     {
1904         if (*we == separator)
1905         {
1906             out->push_back(TextRange(wb, we));
1907             wb = we + 1;
1908         }
1909         we++;
1910     }
1911     if (wb != we)
1912         out->push_back(TextRange(wb, we));
1913 }
1914 
Build()1915 void ImGuiTextFilter::Build()
1916 {
1917     Filters.resize(0);
1918     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1919     input_range.split(',', &Filters);
1920 
1921     CountGrep = 0;
1922     for (int i = 0; i != Filters.Size; i++)
1923     {
1924         TextRange& f = Filters[i];
1925         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1926             f.b++;
1927         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1928             f.e--;
1929         if (f.empty())
1930             continue;
1931         if (Filters[i].b[0] != '-')
1932             CountGrep += 1;
1933     }
1934 }
1935 
PassFilter(const char * text,const char * text_end) const1936 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1937 {
1938     if (Filters.empty())
1939         return true;
1940 
1941     if (text == NULL)
1942         text = "";
1943 
1944     for (int i = 0; i != Filters.Size; i++)
1945     {
1946         const TextRange& f = Filters[i];
1947         if (f.empty())
1948             continue;
1949         if (f.b[0] == '-')
1950         {
1951             // Subtract
1952             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1953                 return false;
1954         }
1955         else
1956         {
1957             // Grep
1958             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1959                 return true;
1960         }
1961     }
1962 
1963     // Implicit * grep
1964     if (CountGrep == 0)
1965         return true;
1966 
1967     return false;
1968 }
1969 
1970 //-----------------------------------------------------------------------------
1971 // [SECTION] ImGuiTextBuffer
1972 //-----------------------------------------------------------------------------
1973 
1974 // On some platform vsnprintf() takes va_list by reference and modifies it.
1975 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1976 #ifndef va_copy
1977 #if defined(__GNUC__) || defined(__clang__)
1978 #define va_copy(dest, src) __builtin_va_copy(dest, src)
1979 #else
1980 #define va_copy(dest, src) (dest = src)
1981 #endif
1982 #endif
1983 
1984 char ImGuiTextBuffer::EmptyString[1] = { 0 };
1985 
1986 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)1987 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
1988 {
1989     va_list args_copy;
1990     va_copy(args_copy, args);
1991 
1992     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1993     if (len <= 0)
1994     {
1995         va_end(args_copy);
1996         return;
1997     }
1998 
1999     // Add zero-terminator the first time
2000     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2001     const int needed_sz = write_off + len;
2002     if (write_off + len >= Buf.Capacity)
2003     {
2004         int double_capacity = Buf.Capacity * 2;
2005         Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
2006     }
2007 
2008     Buf.resize(needed_sz);
2009     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2010     va_end(args_copy);
2011 }
2012 
appendf(const char * fmt,...)2013 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2014 {
2015     va_list args;
2016     va_start(args, fmt);
2017     appendfv(fmt, args);
2018     va_end(args);
2019 }
2020 
2021 //-----------------------------------------------------------------------------
2022 // [SECTION] ImGuiListClipper
2023 // This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
2024 //-----------------------------------------------------------------------------
2025 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2026 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2027 {
2028     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2029     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2030     // The clipper should probably have a 4th step to display the last item in a regular manner.
2031     ImGui::SetCursorPosY(pos_y);
2032     ImGuiWindow* window = ImGui::GetCurrentWindow();
2033     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;      // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.
2034     window->DC.PrevLineSize.y = (line_height - GImGui->Style.ItemSpacing.y);    // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
2035     if (window->DC.ColumnsSet)
2036         window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;           // Setting this so that cell Y position are set properly
2037 }
2038 
2039 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2040 // Use case B: Begin() called from constructor with items_height>0
2041 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)2042 void ImGuiListClipper::Begin(int count, float items_height)
2043 {
2044     StartPosY = ImGui::GetCursorPosY();
2045     ItemsHeight = items_height;
2046     ItemsCount = count;
2047     StepNo = 0;
2048     DisplayEnd = DisplayStart = -1;
2049     if (ItemsHeight > 0.0f)
2050     {
2051         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2052         if (DisplayStart > 0)
2053             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2054         StepNo = 2;
2055     }
2056 }
2057 
End()2058 void ImGuiListClipper::End()
2059 {
2060     if (ItemsCount < 0)
2061         return;
2062     // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
2063     if (ItemsCount < INT_MAX)
2064         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2065     ItemsCount = -1;
2066     StepNo = 3;
2067 }
2068 
Step()2069 bool ImGuiListClipper::Step()
2070 {
2071     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2072     {
2073         ItemsCount = -1;
2074         return false;
2075     }
2076     if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
2077     {
2078         DisplayStart = 0;
2079         DisplayEnd = 1;
2080         StartPosY = ImGui::GetCursorPosY();
2081         StepNo = 1;
2082         return true;
2083     }
2084     if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
2085     {
2086         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2087         float items_height = ImGui::GetCursorPosY() - StartPosY;
2088         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2089         Begin(ItemsCount-1, items_height);
2090         DisplayStart++;
2091         DisplayEnd++;
2092         StepNo = 3;
2093         return true;
2094     }
2095     if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2096     {
2097         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2098         StepNo = 3;
2099         return true;
2100     }
2101     if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
2102         End();
2103     return false;
2104 }
2105 
2106 //-----------------------------------------------------------------------------
2107 // [SECTION] RENDER HELPERS
2108 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2109 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2110 //-----------------------------------------------------------------------------
2111 
FindRenderedTextEnd(const char * text,const char * text_end)2112 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2113 {
2114     const char* text_display_end = text;
2115     if (!text_end)
2116         text_end = (const char*)-1;
2117 
2118     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2119         text_display_end++;
2120     return text_display_end;
2121 }
2122 
2123 // Internal ImGui functions to render text
2124 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2125 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2126 {
2127     ImGuiContext& g = *GImGui;
2128     ImGuiWindow* window = g.CurrentWindow;
2129 
2130     // Hide anything after a '##' string
2131     const char* text_display_end;
2132     if (hide_text_after_hash)
2133     {
2134         text_display_end = FindRenderedTextEnd(text, text_end);
2135     }
2136     else
2137     {
2138         if (!text_end)
2139             text_end = text + strlen(text); // FIXME-OPT
2140         text_display_end = text_end;
2141     }
2142 
2143     if (text != text_display_end)
2144     {
2145         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2146         if (g.LogEnabled)
2147             LogRenderedText(&pos, text, text_display_end);
2148     }
2149 }
2150 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2151 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2152 {
2153     ImGuiContext& g = *GImGui;
2154     ImGuiWindow* window = g.CurrentWindow;
2155 
2156     if (!text_end)
2157         text_end = text + strlen(text); // FIXME-OPT
2158 
2159     if (text != text_end)
2160     {
2161         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2162         if (g.LogEnabled)
2163             LogRenderedText(&pos, text, text_end);
2164     }
2165 }
2166 
2167 // Default clip_rect uses (pos_min,pos_max)
2168 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
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)2169 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)
2170 {
2171     // Hide anything after a '##' string
2172     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2173     const int text_len = (int)(text_display_end - text);
2174     if (text_len == 0)
2175         return;
2176 
2177     ImGuiContext& g = *GImGui;
2178     ImGuiWindow* window = g.CurrentWindow;
2179 
2180     // Perform CPU side clipping for single clipped element to avoid using scissor state
2181     ImVec2 pos = pos_min;
2182     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2183 
2184     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2185     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2186     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2187     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2188         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2189 
2190     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2191     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2192     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2193 
2194     // Render
2195     if (need_clipping)
2196     {
2197         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2198         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2199     }
2200     else
2201     {
2202         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2203     }
2204     if (g.LogEnabled)
2205         LogRenderedText(&pos, text, text_display_end);
2206 }
2207 
2208 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2209 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2210 {
2211     ImGuiContext& g = *GImGui;
2212     ImGuiWindow* window = g.CurrentWindow;
2213     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2214     const float border_size = g.Style.FrameBorderSize;
2215     if (border && border_size > 0.0f)
2216     {
2217         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2218         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2219     }
2220 }
2221 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2222 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2223 {
2224     ImGuiContext& g = *GImGui;
2225     ImGuiWindow* window = g.CurrentWindow;
2226     const float border_size = g.Style.FrameBorderSize;
2227     if (border_size > 0.0f)
2228     {
2229         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2230         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2231     }
2232 }
2233 
2234 // Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
RenderArrow(ImVec2 p_min,ImGuiDir dir,float scale)2235 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2236 {
2237     ImGuiContext& g = *GImGui;
2238 
2239     const float h = g.FontSize * 1.00f;
2240     float r = h * 0.40f * scale;
2241     ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2242 
2243     ImVec2 a, b, c;
2244     switch (dir)
2245     {
2246     case ImGuiDir_Up:
2247     case ImGuiDir_Down:
2248         if (dir == ImGuiDir_Up) r = -r;
2249         a = ImVec2(+0.000f,+0.750f) * r;
2250         b = ImVec2(-0.866f,-0.750f) * r;
2251         c = ImVec2(+0.866f,-0.750f) * r;
2252         break;
2253     case ImGuiDir_Left:
2254     case ImGuiDir_Right:
2255         if (dir == ImGuiDir_Left) r = -r;
2256         a = ImVec2(+0.750f,+0.000f) * r;
2257         b = ImVec2(-0.750f,+0.866f) * r;
2258         c = ImVec2(-0.750f,-0.866f) * r;
2259         break;
2260     case ImGuiDir_None:
2261     case ImGuiDir_COUNT:
2262         IM_ASSERT(0);
2263         break;
2264     }
2265 
2266     g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
2267 }
2268 
RenderBullet(ImVec2 pos)2269 void ImGui::RenderBullet(ImVec2 pos)
2270 {
2271     ImGuiContext& g = *GImGui;
2272     ImGuiWindow* window = g.CurrentWindow;
2273     window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
2274 }
2275 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2276 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2277 {
2278     ImGuiContext& g = *GImGui;
2279     ImGuiWindow* window = g.CurrentWindow;
2280 
2281     float thickness = ImMax(sz / 5.0f, 1.0f);
2282     sz -= thickness*0.5f;
2283     pos += ImVec2(thickness*0.25f, thickness*0.25f);
2284 
2285     float third = sz / 3.0f;
2286     float bx = pos.x + third;
2287     float by = pos.y + sz - third*0.5f;
2288     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2289     window->DrawList->PathLineTo(ImVec2(bx, by));
2290     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2291     window->DrawList->PathStroke(col, false, thickness);
2292 }
2293 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2294 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2295 {
2296     ImGuiContext& g = *GImGui;
2297     if (id != g.NavId)
2298         return;
2299     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2300         return;
2301     ImGuiWindow* window = g.CurrentWindow;
2302     if (window->DC.NavHideHighlightOneFrame)
2303         return;
2304 
2305     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2306     ImRect display_rect = bb;
2307     display_rect.ClipWith(window->ClipRect);
2308     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2309     {
2310         const float THICKNESS = 2.0f;
2311         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2312         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2313         bool fully_visible = window->ClipRect.Contains(display_rect);
2314         if (!fully_visible)
2315             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2316         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);
2317         if (!fully_visible)
2318             window->DrawList->PopClipRect();
2319     }
2320     if (flags & ImGuiNavHighlightFlags_TypeThin)
2321     {
2322         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2323     }
2324 }
2325 
2326 //-----------------------------------------------------------------------------
2327 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2328 //-----------------------------------------------------------------------------
2329 
2330 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2331 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2332     : DrawListInst(&context->DrawListSharedData)
2333 {
2334     Name = ImStrdup(name);
2335     ID = ImHash(name, 0);
2336     IDStack.push_back(ID);
2337     Flags = 0;
2338     Pos = ImVec2(0.0f, 0.0f);
2339     Size = SizeFull = ImVec2(0.0f, 0.0f);
2340     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2341     WindowPadding = ImVec2(0.0f, 0.0f);
2342     WindowRounding = 0.0f;
2343     WindowBorderSize = 0.0f;
2344     MoveId = GetID("#MOVE");
2345     ChildId = 0;
2346     Scroll = ImVec2(0.0f, 0.0f);
2347     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2348     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2349     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2350     ScrollbarX = ScrollbarY = false;
2351     Active = WasActive = false;
2352     WriteAccessed = false;
2353     Collapsed = false;
2354     WantCollapseToggle = false;
2355     SkipItems = false;
2356     Appearing = false;
2357     Hidden = false;
2358     HasCloseButton = false;
2359     BeginCount = 0;
2360     BeginOrderWithinParent = -1;
2361     BeginOrderWithinContext = -1;
2362     PopupId = 0;
2363     AutoFitFramesX = AutoFitFramesY = -1;
2364     AutoFitOnlyGrows = false;
2365     AutoFitChildAxises = 0x00;
2366     AutoPosLastDirection = ImGuiDir_None;
2367     HiddenFramesRegular = HiddenFramesForResize = 0;
2368     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2369     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2370 
2371     LastFrameActive = -1;
2372     ItemWidthDefault = 0.0f;
2373     FontWindowScale = 1.0f;
2374     SettingsIdx = -1;
2375 
2376     DrawList = &DrawListInst;
2377     DrawList->_OwnerName = Name;
2378     ParentWindow = NULL;
2379     RootWindow = NULL;
2380     RootWindowForTitleBarHighlight = NULL;
2381     RootWindowForNav = NULL;
2382 
2383     NavLastIds[0] = NavLastIds[1] = 0;
2384     NavRectRel[0] = NavRectRel[1] = ImRect();
2385     NavLastChildNavWindow = NULL;
2386 
2387     FocusIdxAllCounter = FocusIdxTabCounter = -1;
2388     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2389     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2390 }
2391 
~ImGuiWindow()2392 ImGuiWindow::~ImGuiWindow()
2393 {
2394     IM_ASSERT(DrawList == &DrawListInst);
2395     IM_DELETE(Name);
2396     for (int i = 0; i != ColumnsStorage.Size; i++)
2397         ColumnsStorage[i].~ImGuiColumnsSet();
2398 }
2399 
GetID(const char * str,const char * str_end)2400 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2401 {
2402     ImGuiID seed = IDStack.back();
2403     ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2404     ImGui::KeepAliveID(id);
2405     return id;
2406 }
2407 
GetID(const void * ptr)2408 ImGuiID ImGuiWindow::GetID(const void* ptr)
2409 {
2410     ImGuiID seed = IDStack.back();
2411     ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
2412     ImGui::KeepAliveID(id);
2413     return id;
2414 }
2415 
GetIDNoKeepAlive(const char * str,const char * str_end)2416 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2417 {
2418     ImGuiID seed = IDStack.back();
2419     return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2420 }
2421 
GetIDNoKeepAlive(const void * ptr)2422 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2423 {
2424     ImGuiID seed = IDStack.back();
2425     return ImHash(&ptr, sizeof(void*), seed);
2426 }
2427 
2428 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2429 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2430 {
2431     ImGuiID seed = IDStack.back();
2432     const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) };
2433     ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
2434     ImGui::KeepAliveID(id);
2435     return id;
2436 }
2437 
SetCurrentWindow(ImGuiWindow * window)2438 static void SetCurrentWindow(ImGuiWindow* window)
2439 {
2440     ImGuiContext& g = *GImGui;
2441     g.CurrentWindow = window;
2442     if (window)
2443         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2444 }
2445 
SetNavID(ImGuiID id,int nav_layer)2446 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2447 {
2448     ImGuiContext& g = *GImGui;
2449     IM_ASSERT(g.NavWindow);
2450     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2451     g.NavId = id;
2452     g.NavWindow->NavLastIds[nav_layer] = id;
2453 }
2454 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2455 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2456 {
2457     ImGuiContext& g = *GImGui;
2458     SetNavID(id, nav_layer);
2459     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2460     g.NavMousePosDirty = true;
2461     g.NavDisableHighlight = false;
2462     g.NavDisableMouseHover = true;
2463 }
2464 
SetActiveID(ImGuiID id,ImGuiWindow * window)2465 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2466 {
2467     ImGuiContext& g = *GImGui;
2468     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2469     if (g.ActiveIdIsJustActivated)
2470     {
2471         g.ActiveIdTimer = 0.0f;
2472         g.ActiveIdHasBeenEdited = false;
2473         if (id != 0)
2474         {
2475             g.LastActiveId = id;
2476             g.LastActiveIdTimer = 0.0f;
2477         }
2478     }
2479     g.ActiveId = id;
2480     g.ActiveIdAllowNavDirFlags = 0;
2481     g.ActiveIdAllowOverlap = false;
2482     g.ActiveIdWindow = window;
2483     if (id)
2484     {
2485         g.ActiveIdIsAlive = id;
2486         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2487     }
2488 }
2489 
SetFocusID(ImGuiID id,ImGuiWindow * window)2490 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2491 {
2492     ImGuiContext& g = *GImGui;
2493     IM_ASSERT(id != 0);
2494 
2495     // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2496     const int nav_layer = window->DC.NavLayerCurrent;
2497     if (g.NavWindow != window)
2498         g.NavInitRequest = false;
2499     g.NavId = id;
2500     g.NavWindow = window;
2501     g.NavLayer = nav_layer;
2502     window->NavLastIds[nav_layer] = id;
2503     if (window->DC.LastItemId == id)
2504         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2505 
2506     if (g.ActiveIdSource == ImGuiInputSource_Nav)
2507         g.NavDisableMouseHover = true;
2508     else
2509         g.NavDisableHighlight = true;
2510 }
2511 
ClearActiveID()2512 void ImGui::ClearActiveID()
2513 {
2514     SetActiveID(0, NULL);
2515 }
2516 
SetHoveredID(ImGuiID id)2517 void ImGui::SetHoveredID(ImGuiID id)
2518 {
2519     ImGuiContext& g = *GImGui;
2520     g.HoveredId = id;
2521     g.HoveredIdAllowOverlap = false;
2522     if (id != 0 && g.HoveredIdPreviousFrame != id)
2523         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2524 }
2525 
GetHoveredID()2526 ImGuiID ImGui::GetHoveredID()
2527 {
2528     ImGuiContext& g = *GImGui;
2529     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2530 }
2531 
KeepAliveID(ImGuiID id)2532 void ImGui::KeepAliveID(ImGuiID id)
2533 {
2534     ImGuiContext& g = *GImGui;
2535     if (g.ActiveId == id)
2536         g.ActiveIdIsAlive = id;
2537     if (g.ActiveIdPreviousFrame == id)
2538         g.ActiveIdPreviousFrameIsAlive = true;
2539 }
2540 
MarkItemEdited(ImGuiID id)2541 void ImGui::MarkItemEdited(ImGuiID id)
2542 {
2543     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2544     // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data.
2545     (void)id; // Avoid unused variable warnings when asserts are compiled out.
2546     ImGuiContext& g = *GImGui;
2547     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2548     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2549     g.ActiveIdHasBeenEdited = true;
2550     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2551 }
2552 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2553 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2554 {
2555     // An active popup disable hovering on other windows (apart from its own children)
2556     // FIXME-OPT: This could be cached/stored within the window.
2557     ImGuiContext& g = *GImGui;
2558     if (g.NavWindow)
2559         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2560             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2561             {
2562                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2563                 // NB: The order of those two tests is important because Modal windows are also Popups.
2564                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2565                     return false;
2566                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2567                     return false;
2568             }
2569 
2570     return true;
2571 }
2572 
2573 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2574 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2575 {
2576     ImGuiContext& g = *GImGui;
2577     ImGuiWindow* window = g.CurrentWindow;
2578     if (window->SkipItems)
2579         return;
2580 
2581     // Always align ourselves on pixel boundaries
2582     const float line_height = ImMax(window->DC.CurrentLineSize.y, size.y);
2583     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2584     //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
2585     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2586     window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
2587     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2588     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2589     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2590 
2591     window->DC.PrevLineSize.y = line_height;
2592     window->DC.PrevLineTextBaseOffset = text_base_offset;
2593     window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2594 
2595     // Horizontal layout mode
2596     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2597         SameLine();
2598 }
2599 
ItemSize(const ImRect & bb,float text_offset_y)2600 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2601 {
2602     ItemSize(bb.GetSize(), text_offset_y);
2603 }
2604 
2605 // Declare item bounding box for clipping and interaction.
2606 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2607 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)2608 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2609 {
2610 #ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
2611     ImGuiTestEngineHook_ItemAdd(bb, id, nav_bb_arg);
2612 #endif
2613 
2614     ImGuiContext& g = *GImGui;
2615     ImGuiWindow* window = g.CurrentWindow;
2616 
2617     if (id != 0)
2618     {
2619         // Navigation processing runs prior to clipping early-out
2620         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2621         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window.
2622         //      it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2623         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick)
2624         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2625         if (g.NavId == id || g.NavAnyRequest)
2626             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2627                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2628                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2629     }
2630 
2631     window->DC.LastItemId = id;
2632     window->DC.LastItemRect = bb;
2633     window->DC.LastItemStatusFlags = 0;
2634 
2635     // Clipping test
2636     const bool is_clipped = IsClippedEx(bb, id, false);
2637     if (is_clipped)
2638         return false;
2639     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2640 
2641     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2642     if (IsMouseHoveringRect(bb.Min, bb.Max))
2643         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2644     return true;
2645 }
2646 
2647 // This is roughly matching the behavior of internal-facing ItemHoverable()
2648 // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
2649 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2650 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2651 {
2652     ImGuiContext& g = *GImGui;
2653     ImGuiWindow* window = g.CurrentWindow;
2654     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2655         return IsItemFocused();
2656 
2657     // Test for bounding box overlap, as updated as ItemAdd()
2658     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2659         return false;
2660     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2661 
2662     // Test if we are hovering the right window (our window could be behind another window)
2663     // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
2664     // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
2665     //if (g.HoveredWindow != window)
2666     //    return false;
2667     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2668         return false;
2669 
2670     // Test if another item is active (e.g. being dragged)
2671     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2672         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2673             return false;
2674 
2675     // Test if interactions on this window are blocked by an active popup or modal
2676     if (!IsWindowContentHoverable(window, flags))
2677         return false;
2678 
2679     // Test if the item is disabled
2680     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2681         return false;
2682 
2683     // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
2684     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2685         return false;
2686     return true;
2687 }
2688 
2689 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2690 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2691 {
2692     ImGuiContext& g = *GImGui;
2693     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2694         return false;
2695 
2696     ImGuiWindow* window = g.CurrentWindow;
2697     if (g.HoveredWindow != window)
2698         return false;
2699     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2700         return false;
2701     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2702         return false;
2703     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2704         return false;
2705     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2706         return false;
2707 
2708     SetHoveredID(id);
2709     return true;
2710 }
2711 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2712 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2713 {
2714     ImGuiContext& g = *GImGui;
2715     ImGuiWindow* window = g.CurrentWindow;
2716     if (!bb.Overlaps(window->ClipRect))
2717         if (id == 0 || id != g.ActiveId)
2718             if (clip_even_when_logged || !g.LogEnabled)
2719                 return true;
2720     return false;
2721 }
2722 
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2723 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2724 {
2725     ImGuiContext& g = *GImGui;
2726 
2727     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2728     window->FocusIdxAllCounter++;
2729     if (is_tab_stop)
2730         window->FocusIdxTabCounter++;
2731 
2732     // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2733     // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2734     if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2735         window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2736 
2737     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2738         return true;
2739     if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2740     {
2741         g.NavJustTabbedId = id;
2742         return true;
2743     }
2744 
2745     return false;
2746 }
2747 
FocusableItemUnregister(ImGuiWindow * window)2748 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2749 {
2750     window->FocusIdxAllCounter--;
2751     window->FocusIdxTabCounter--;
2752 }
2753 
CalcItemSize(ImVec2 size,float default_x,float default_y)2754 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2755 {
2756     ImGuiContext& g = *GImGui;
2757     ImVec2 content_max;
2758     if (size.x < 0.0f || size.y < 0.0f)
2759         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2760     if (size.x <= 0.0f)
2761         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2762     if (size.y <= 0.0f)
2763         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2764     return size;
2765 }
2766 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2767 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2768 {
2769     if (wrap_pos_x < 0.0f)
2770         return 0.0f;
2771 
2772     ImGuiWindow* window = GetCurrentWindowRead();
2773     if (wrap_pos_x == 0.0f)
2774         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2775     else if (wrap_pos_x > 0.0f)
2776         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2777 
2778     return ImMax(wrap_pos_x - pos.x, 1.0f);
2779 }
2780 
MemAlloc(size_t size)2781 void* ImGui::MemAlloc(size_t size)
2782 {
2783     if (ImGuiContext* ctx = GImGui)
2784         ctx->IO.MetricsActiveAllocations++;
2785     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2786 }
2787 
MemFree(void * ptr)2788 void ImGui::MemFree(void* ptr)
2789 {
2790     if (ptr)
2791         if (ImGuiContext* ctx = GImGui)
2792             ctx->IO.MetricsActiveAllocations--;
2793     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2794 }
2795 
GetClipboardText()2796 const char* ImGui::GetClipboardText()
2797 {
2798     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2799 }
2800 
SetClipboardText(const char * text)2801 void ImGui::SetClipboardText(const char* text)
2802 {
2803     if (GImGui->IO.SetClipboardTextFn)
2804         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2805 }
2806 
GetVersion()2807 const char* ImGui::GetVersion()
2808 {
2809     return IMGUI_VERSION;
2810 }
2811 
2812 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2813 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
GetCurrentContext()2814 ImGuiContext* ImGui::GetCurrentContext()
2815 {
2816     return GImGui;
2817 }
2818 
SetCurrentContext(ImGuiContext * ctx)2819 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2820 {
2821 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2822     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2823 #else
2824     GImGui = ctx;
2825 #endif
2826 }
2827 
2828 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
2829 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic.
DebugCheckVersionAndDataLayout(const char * version,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_vert)2830 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)
2831 {
2832     bool error = false;
2833     if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!");  }
2834     if (sz_io    != sizeof(ImGuiIO))       { error = true; IM_ASSERT(sz_io    == sizeof(ImGuiIO)      && "Mismatched struct layout!"); }
2835     if (sz_style != sizeof(ImGuiStyle))    { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle)   && "Mismatched struct layout!"); }
2836     if (sz_vec2  != sizeof(ImVec2))        { error = true; IM_ASSERT(sz_vec2  == sizeof(ImVec2)       && "Mismatched struct layout!"); }
2837     if (sz_vec4  != sizeof(ImVec4))        { error = true; IM_ASSERT(sz_vec4  == sizeof(ImVec4)       && "Mismatched struct layout!"); }
2838     if (sz_vert  != sizeof(ImDrawVert))    { error = true; IM_ASSERT(sz_vert  == sizeof(ImDrawVert)   && "Mismatched struct layout!"); }
2839     return !error;
2840 }
2841 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)2842 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
2843 {
2844     GImAllocatorAllocFunc = alloc_func;
2845     GImAllocatorFreeFunc = free_func;
2846     GImAllocatorUserData = user_data;
2847 }
2848 
CreateContext(ImFontAtlas * shared_font_atlas)2849 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2850 {
2851     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2852     if (GImGui == NULL)
2853         SetCurrentContext(ctx);
2854     Initialize(ctx);
2855     return ctx;
2856 }
2857 
DestroyContext(ImGuiContext * ctx)2858 void ImGui::DestroyContext(ImGuiContext* ctx)
2859 {
2860     if (ctx == NULL)
2861         ctx = GImGui;
2862     Shutdown(ctx);
2863     if (GImGui == ctx)
2864         SetCurrentContext(NULL);
2865     IM_DELETE(ctx);
2866 }
2867 
GetIO()2868 ImGuiIO& ImGui::GetIO()
2869 {
2870     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2871     return GImGui->IO;
2872 }
2873 
GetStyle()2874 ImGuiStyle& ImGui::GetStyle()
2875 {
2876     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2877     return GImGui->Style;
2878 }
2879 
2880 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()2881 ImDrawData* ImGui::GetDrawData()
2882 {
2883     ImGuiContext& g = *GImGui;
2884     return g.DrawData.Valid ? &g.DrawData : NULL;
2885 }
2886 
GetTime()2887 double ImGui::GetTime()
2888 {
2889     return GImGui->Time;
2890 }
2891 
GetFrameCount()2892 int ImGui::GetFrameCount()
2893 {
2894     return GImGui->FrameCount;
2895 }
2896 
GetOverlayDrawList(ImGuiWindow *)2897 static ImDrawList* GetOverlayDrawList(ImGuiWindow*)
2898 {
2899     // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches.
2900     return &GImGui->OverlayDrawList;
2901 }
2902 
GetOverlayDrawList()2903 ImDrawList* ImGui::GetOverlayDrawList()
2904 {
2905     return &GImGui->OverlayDrawList;
2906 }
2907 
GetDrawListSharedData()2908 ImDrawListSharedData* ImGui::GetDrawListSharedData()
2909 {
2910     return &GImGui->DrawListSharedData;
2911 }
2912 
StartMouseMovingWindow(ImGuiWindow * window)2913 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
2914 {
2915     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
2916     ImGuiContext& g = *GImGui;
2917     FocusWindow(window);
2918     SetActiveID(window->MoveId, window);
2919     g.NavDisableHighlight = true;
2920     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
2921     if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
2922         g.MovingWindow = window;
2923 }
2924 
2925 // Handle mouse moving window
2926 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindow()2927 void ImGui::UpdateMouseMovingWindow()
2928 {
2929     ImGuiContext& g = *GImGui;
2930     if (g.MovingWindow != NULL)
2931     {
2932         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
2933         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
2934         KeepAliveID(g.ActiveId);
2935         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
2936         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
2937         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
2938         {
2939             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
2940             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
2941             {
2942                 MarkIniSettingsDirty(moving_window);
2943                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
2944             }
2945             FocusWindow(g.MovingWindow);
2946         }
2947         else
2948         {
2949             ClearActiveID();
2950             g.MovingWindow = NULL;
2951         }
2952     }
2953     else
2954     {
2955         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
2956         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
2957         {
2958             KeepAliveID(g.ActiveId);
2959             if (!g.IO.MouseDown[0])
2960                 ClearActiveID();
2961         }
2962     }
2963 }
2964 
IsWindowActiveAndVisible(ImGuiWindow * window)2965 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
2966 {
2967     return (window->Active) && (!window->Hidden);
2968 }
2969 
UpdateMouseInputs()2970 static void ImGui::UpdateMouseInputs()
2971 {
2972     ImGuiContext& g = *GImGui;
2973 
2974     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
2975     if (IsMousePosValid(&g.IO.MousePos))
2976         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
2977 
2978     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
2979     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
2980         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2981     else
2982         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2983     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
2984         g.NavDisableMouseHover = false;
2985 
2986     g.IO.MousePosPrev = g.IO.MousePos;
2987     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2988     {
2989         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2990         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2991         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2992         g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
2993         g.IO.MouseDoubleClicked[i] = false;
2994         if (g.IO.MouseClicked[i])
2995         {
2996             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
2997             {
2998                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
2999                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3000                     g.IO.MouseDoubleClicked[i] = true;
3001                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
3002             }
3003             else
3004             {
3005                 g.IO.MouseClickedTime[i] = g.Time;
3006             }
3007             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3008             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3009             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3010         }
3011         else if (g.IO.MouseDown[i])
3012         {
3013             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3014             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3015             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3016             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);
3017             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);
3018         }
3019         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3020             g.NavDisableMouseHover = false;
3021     }
3022 }
3023 
UpdateMouseWheel()3024 void ImGui::UpdateMouseWheel()
3025 {
3026     ImGuiContext& g = *GImGui;
3027     if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
3028         return;
3029     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3030         return;
3031 
3032     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set).
3033     ImGuiWindow* window = g.HoveredWindow;
3034     ImGuiWindow* scroll_window = window;
3035     while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs) && scroll_window->ParentWindow)
3036         scroll_window = scroll_window->ParentWindow;
3037     const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs);
3038 
3039     if (g.IO.MouseWheel != 0.0f)
3040     {
3041         if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3042         {
3043             // Zoom / Scale window
3044             const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3045             const float scale = new_font_scale / window->FontWindowScale;
3046             window->FontWindowScale = new_font_scale;
3047 
3048             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3049             window->Pos += offset;
3050             window->Size *= scale;
3051             window->SizeFull *= scale;
3052         }
3053         else if (!g.IO.KeyCtrl && scroll_allowed)
3054         {
3055             // Mouse wheel vertical scrolling
3056             float scroll_amount = 5 * scroll_window->CalcFontSize();
3057             scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3058             SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3059         }
3060     }
3061     if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3062     {
3063         // Mouse wheel horizontal scrolling (for hardware that supports it)
3064         float scroll_amount = scroll_window->CalcFontSize();
3065         SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3066     }
3067 }
3068 
3069 // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
UpdateHoveredWindowAndCaptureFlags()3070 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3071 {
3072     ImGuiContext& g = *GImGui;
3073 
3074     // Find the window hovered by mouse:
3075     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3076     // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
3077     // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
3078     FindHoveredWindow();
3079 
3080     // Modal windows prevents cursor from hovering behind them.
3081     ImGuiWindow* modal_window = GetFrontMostPopupModal();
3082     if (modal_window)
3083         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3084             g.HoveredRootWindow = g.HoveredWindow = NULL;
3085 
3086     // Disabled mouse?
3087     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3088         g.HoveredWindow = g.HoveredRootWindow = NULL;
3089 
3090     // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
3091     int mouse_earliest_button_down = -1;
3092     bool mouse_any_down = false;
3093     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3094     {
3095         if (g.IO.MouseClicked[i])
3096             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3097         mouse_any_down |= g.IO.MouseDown[i];
3098         if (g.IO.MouseDown[i])
3099             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3100                 mouse_earliest_button_down = i;
3101     }
3102     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3103 
3104     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3105     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3106     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3107     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3108         g.HoveredWindow = g.HoveredRootWindow = NULL;
3109 
3110     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3111     if (g.WantCaptureMouseNextFrame != -1)
3112         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3113     else
3114         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3115 
3116     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3117     if (g.WantCaptureKeyboardNextFrame != -1)
3118         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3119     else
3120         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3121     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3122         g.IO.WantCaptureKeyboard = true;
3123 
3124     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3125     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3126 }
3127 
NewFrame()3128 void ImGui::NewFrame()
3129 {
3130     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3131     ImGuiContext& g = *GImGui;
3132 
3133 #ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
3134     ImGuiTestEngineHook_PreNewFrame();
3135 #endif
3136 
3137     // Check user data
3138     // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)
3139     IM_ASSERT(g.Initialized);
3140     IM_ASSERT(g.IO.DeltaTime >= 0.0f                                    && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3141     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value");
3142     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3143     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3144     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting");
3145     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)");
3146     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3147     for (int n = 0; n < ImGuiKey_COUNT; n++)
3148         IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");
3149 
3150     // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
3151     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3152         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3153 
3154     // Perform simple check: the beta io.ConfigResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3155     if (g.IO.ConfigResizeWindowsFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3156         g.IO.ConfigResizeWindowsFromEdges = false;
3157 
3158     // Load settings on first frame (if not explicitly loaded manually before)
3159     if (!g.SettingsLoaded)
3160     {
3161         IM_ASSERT(g.SettingsWindows.empty());
3162         if (g.IO.IniFilename)
3163             LoadIniSettingsFromDisk(g.IO.IniFilename);
3164         g.SettingsLoaded = true;
3165     }
3166 
3167     // Save settings (with a delay after the last modification, so we don't spam disk too much)
3168     if (g.SettingsDirtyTimer > 0.0f)
3169     {
3170         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3171         if (g.SettingsDirtyTimer <= 0.0f)
3172         {
3173             if (g.IO.IniFilename != NULL)
3174                 SaveIniSettingsToDisk(g.IO.IniFilename);
3175             else
3176                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3177             g.SettingsDirtyTimer = 0.0f;
3178         }
3179     }
3180 
3181     g.Time += g.IO.DeltaTime;
3182     g.FrameScopeActive = true;
3183     g.FrameCount += 1;
3184     g.TooltipOverrideCount = 0;
3185     g.WindowsActiveCount = 0;
3186 
3187     // Setup current font and draw list
3188     g.IO.Fonts->Locked = true;
3189     SetCurrentFont(GetDefaultFont());
3190     IM_ASSERT(g.Font->IsLoaded());
3191     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3192     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3193 
3194     g.OverlayDrawList.Clear();
3195     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3196     g.OverlayDrawList.PushClipRectFullScreen();
3197     g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3198 
3199     // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3200     g.DrawData.Clear();
3201 
3202     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3203     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3204         KeepAliveID(g.DragDropPayload.SourceId);
3205 
3206     // Clear reference to active widget if the widget isn't alive anymore
3207     if (!g.HoveredIdPreviousFrame)
3208         g.HoveredIdTimer = 0.0f;
3209     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3210         g.HoveredIdNotActiveTimer = 0.0f;
3211     if (g.HoveredId)
3212         g.HoveredIdTimer += g.IO.DeltaTime;
3213     if (g.HoveredId && g.ActiveId != g.HoveredId)
3214         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3215     g.HoveredIdPreviousFrame = g.HoveredId;
3216     g.HoveredId = 0;
3217     g.HoveredIdAllowOverlap = false;
3218     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3219         ClearActiveID();
3220     if (g.ActiveId)
3221         g.ActiveIdTimer += g.IO.DeltaTime;
3222     g.LastActiveIdTimer += g.IO.DeltaTime;
3223     g.ActiveIdPreviousFrame = g.ActiveId;
3224     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3225     g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3226     g.ActiveIdIsAlive = 0;
3227     g.ActiveIdPreviousFrameIsAlive = false;
3228     g.ActiveIdIsJustActivated = false;
3229     if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3230         g.ScalarAsInputTextId = 0;
3231 
3232     // Drag and drop
3233     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3234     g.DragDropAcceptIdCurr = 0;
3235     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3236     g.DragDropWithinSourceOrTarget = false;
3237 
3238     // Update keyboard input state
3239     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3240     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3241         g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
3242 
3243     // Update gamepad/keyboard directional navigation
3244     NavUpdate();
3245 
3246     // Update mouse input state
3247     UpdateMouseInputs();
3248 
3249     // Calculate frame-rate for the user, as a purely luxurious feature
3250     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3251     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3252     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3253     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3254 
3255     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3256     UpdateMouseMovingWindow();
3257     UpdateHoveredWindowAndCaptureFlags();
3258 
3259     // Background darkening/whitening
3260     if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3261         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3262     else
3263         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3264 
3265     g.MouseCursor = ImGuiMouseCursor_Arrow;
3266     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3267     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3268 
3269     // Mouse wheel scrolling, scale
3270     UpdateMouseWheel();
3271 
3272     // Pressing TAB activate widget focus
3273     if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3274     {
3275         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3276             g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3277         else
3278             g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3279     }
3280     g.NavIdTabCounter = INT_MAX;
3281 
3282     // Mark all windows as not visible
3283     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3284     for (int i = 0; i != g.Windows.Size; i++)
3285     {
3286         ImGuiWindow* window = g.Windows[i];
3287         window->WasActive = window->Active;
3288         window->Active = false;
3289         window->WriteAccessed = false;
3290     }
3291 
3292     // Closing the focused window restore focus to the first active root window in descending z-order
3293     if (g.NavWindow && !g.NavWindow->WasActive)
3294         FocusPreviousWindowIgnoringOne(NULL);
3295 
3296     // No window should be open at the beginning of the frame.
3297     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3298     g.CurrentWindowStack.resize(0);
3299     g.CurrentPopupStack.resize(0);
3300     ClosePopupsOverWindow(g.NavWindow);
3301 
3302     // Create implicit window - we will only render it if the user has added something to it.
3303     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3304     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3305     Begin("Debug##Default");
3306 
3307 #ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS
3308     ImGuiTestEngineHook_PostNewFrame();
3309 #endif
3310 }
3311 
Initialize(ImGuiContext * context)3312 void ImGui::Initialize(ImGuiContext* context)
3313 {
3314     ImGuiContext& g = *context;
3315     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3316 
3317     // Add .ini handle for ImGuiWindow type
3318     ImGuiSettingsHandler ini_handler;
3319     ini_handler.TypeName = "Window";
3320     ini_handler.TypeHash = ImHash("Window", 0, 0);
3321     ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3322     ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3323     ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3324     g.SettingsHandlers.push_front(ini_handler);
3325 
3326     g.Initialized = true;
3327 }
3328 
3329 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3330 void ImGui::Shutdown(ImGuiContext* context)
3331 {
3332     // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
3333     ImGuiContext& g = *context;
3334     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3335     {
3336         g.IO.Fonts->Locked = false;
3337         IM_DELETE(g.IO.Fonts);
3338     }
3339     g.IO.Fonts = NULL;
3340 
3341     // Cleanup of other data are conditional on actually having initialized ImGui.
3342     if (!g.Initialized)
3343         return;
3344 
3345     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3346     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3347     {
3348         ImGuiContext* backup_context = GImGui;
3349         SetCurrentContext(context);
3350         SaveIniSettingsToDisk(g.IO.IniFilename);
3351         SetCurrentContext(backup_context);
3352     }
3353 
3354     // Clear everything else
3355     for (int i = 0; i < g.Windows.Size; i++)
3356         IM_DELETE(g.Windows[i]);
3357     g.Windows.clear();
3358     g.WindowsFocusOrder.clear();
3359     g.WindowsSortBuffer.clear();
3360     g.CurrentWindow = NULL;
3361     g.CurrentWindowStack.clear();
3362     g.WindowsById.Clear();
3363     g.NavWindow = NULL;
3364     g.HoveredWindow = NULL;
3365     g.HoveredRootWindow = NULL;
3366     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3367     g.MovingWindow = NULL;
3368     g.ColorModifiers.clear();
3369     g.StyleModifiers.clear();
3370     g.FontStack.clear();
3371     g.OpenPopupStack.clear();
3372     g.CurrentPopupStack.clear();
3373     g.DrawDataBuilder.ClearFreeMemory();
3374     g.OverlayDrawList.ClearFreeMemory();
3375     g.PrivateClipboard.clear();
3376     g.InputTextState.TextW.clear();
3377     g.InputTextState.InitialText.clear();
3378     g.InputTextState.TempBuffer.clear();
3379 
3380     for (int i = 0; i < g.SettingsWindows.Size; i++)
3381         IM_DELETE(g.SettingsWindows[i].Name);
3382     g.SettingsWindows.clear();
3383     g.SettingsHandlers.clear();
3384 
3385     if (g.LogFile && g.LogFile != stdout)
3386     {
3387         fclose(g.LogFile);
3388         g.LogFile = NULL;
3389     }
3390     g.LogClipboard.clear();
3391 
3392     g.Initialized = false;
3393 }
3394 
3395 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3396 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3397 {
3398     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3399     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3400     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3401         return d;
3402     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3403         return d;
3404     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3405 }
3406 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3407 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3408 {
3409     out_sorted_windows->push_back(window);
3410     if (window->Active)
3411     {
3412         int count = window->DC.ChildWindows.Size;
3413         if (count > 1)
3414             ImQsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3415         for (int i = 0; i < count; i++)
3416         {
3417             ImGuiWindow* child = window->DC.ChildWindows[i];
3418             if (child->Active)
3419                 AddWindowToSortBuffer(out_sorted_windows, child);
3420         }
3421     }
3422 }
3423 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3424 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3425 {
3426     if (draw_list->CmdBuffer.empty())
3427         return;
3428 
3429     // Remove trailing command if unused
3430     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3431     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3432     {
3433         draw_list->CmdBuffer.pop_back();
3434         if (draw_list->CmdBuffer.empty())
3435             return;
3436     }
3437 
3438     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
3439     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3440     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3441     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3442 
3443     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3444     // If this assert triggers because you are drawing lots of stuff manually:
3445     // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents.
3446     // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes.
3447     //    You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing:
3448     //      glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3449     //    Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
3450     // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
3451     if (sizeof(ImDrawIdx) == 2)
3452         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3453 
3454     out_list->push_back(draw_list);
3455 }
3456 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3457 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3458 {
3459     ImGuiContext& g = *GImGui;
3460     g.IO.MetricsRenderWindows++;
3461     AddDrawListToDrawData(out_render_list, window->DrawList);
3462     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3463     {
3464         ImGuiWindow* child = window->DC.ChildWindows[i];
3465         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3466             AddWindowToDrawData(out_render_list, child);
3467     }
3468 }
3469 
AddWindowToDrawDataSelectLayer(ImGuiWindow * window)3470 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3471 {
3472     ImGuiContext& g = *GImGui;
3473     if (window->Flags & ImGuiWindowFlags_Tooltip)
3474         AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3475     else
3476         AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3477 }
3478 
FlattenIntoSingleLayer()3479 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3480 {
3481     int n = Layers[0].Size;
3482     int size = n;
3483     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3484         size += Layers[i].Size;
3485     Layers[0].resize(size);
3486     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3487     {
3488         ImVector<ImDrawList*>& layer = Layers[layer_n];
3489         if (layer.empty())
3490             continue;
3491         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3492         n += layer.Size;
3493         layer.resize(0);
3494     }
3495 }
3496 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3497 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3498 {
3499     ImGuiIO& io = ImGui::GetIO();
3500     draw_data->Valid = true;
3501     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3502     draw_data->CmdListsCount = draw_lists->Size;
3503     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3504     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3505     draw_data->DisplaySize = io.DisplaySize;
3506     for (int n = 0; n < draw_lists->Size; n++)
3507     {
3508         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3509         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3510     }
3511 }
3512 
3513 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)3514 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3515 {
3516     ImGuiWindow* window = GetCurrentWindow();
3517     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3518     window->ClipRect = window->DrawList->_ClipRectStack.back();
3519 }
3520 
PopClipRect()3521 void ImGui::PopClipRect()
3522 {
3523     ImGuiWindow* window = GetCurrentWindow();
3524     window->DrawList->PopClipRect();
3525     window->ClipRect = window->DrawList->_ClipRectStack.back();
3526 }
3527 
3528 // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
EndFrame()3529 void ImGui::EndFrame()
3530 {
3531     ImGuiContext& g = *GImGui;
3532     IM_ASSERT(g.Initialized);
3533     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
3534         return;
3535     IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()");
3536 
3537     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3538     if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)
3539     {
3540         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3541         g.PlatformImeLastPos = g.PlatformImePos;
3542     }
3543 
3544     // Hide implicit "Debug" window if it hasn't been used
3545     IM_ASSERT(g.CurrentWindowStack.Size == 1);    // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name?
3546     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3547         g.CurrentWindow->Active = false;
3548     End();
3549 
3550     // Show CTRL+TAB list
3551     if (g.NavWindowingTarget)
3552         NavUpdateWindowingList();
3553 
3554     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3555     if (g.DragDropActive)
3556     {
3557         bool is_delivered = g.DragDropPayload.Delivery;
3558         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
3559         if (is_delivered || is_elapsed)
3560             ClearDragDrop();
3561     }
3562 
3563     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3564     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3565     {
3566         g.DragDropWithinSourceOrTarget = true;
3567         SetTooltip("...");
3568         g.DragDropWithinSourceOrTarget = false;
3569     }
3570 
3571     // Initiate moving window
3572     if (g.ActiveId == 0 && g.HoveredId == 0)
3573     {
3574         if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
3575         {
3576             // Click to focus window and start moving (after we're done with all our widgets)
3577             if (g.IO.MouseClicked[0])
3578             {
3579                 if (g.HoveredRootWindow != NULL)
3580                     StartMouseMovingWindow(g.HoveredWindow);
3581                 else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3582                     FocusWindow(NULL);  // Clicking on void disable focus
3583             }
3584 
3585             // With right mouse button we close popups without changing focus
3586             // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3587             if (g.IO.MouseClicked[1])
3588             {
3589                 // Find the top-most window between HoveredWindow and the front most Modal Window.
3590                 // This is where we can trim the popup stack.
3591                 ImGuiWindow* modal = GetFrontMostPopupModal();
3592                 bool hovered_window_above_modal = false;
3593                 if (modal == NULL)
3594                     hovered_window_above_modal = true;
3595                 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3596                 {
3597                     ImGuiWindow* window = g.Windows[i];
3598                     if (window == modal)
3599                         break;
3600                     if (window == g.HoveredWindow)
3601                         hovered_window_above_modal = true;
3602                 }
3603                 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
3604             }
3605         }
3606     }
3607 
3608     // Sort the window list so that all child windows are after their parent
3609     // We cannot do that on FocusWindow() because childs may not exist yet
3610     g.WindowsSortBuffer.resize(0);
3611     g.WindowsSortBuffer.reserve(g.Windows.Size);
3612     for (int i = 0; i != g.Windows.Size; i++)
3613     {
3614         ImGuiWindow* window = g.Windows[i];
3615         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
3616             continue;
3617         AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
3618     }
3619 
3620     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);  // we done something wrong
3621     g.Windows.swap(g.WindowsSortBuffer);
3622     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3623 
3624     // Unlock font atlas
3625     g.IO.Fonts->Locked = false;
3626 
3627     // Clear Input data for next frame
3628     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3629     memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
3630     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
3631 
3632     g.FrameScopeActive = false;
3633     g.FrameCountEnded = g.FrameCount;
3634 }
3635 
Render()3636 void ImGui::Render()
3637 {
3638     ImGuiContext& g = *GImGui;
3639     IM_ASSERT(g.Initialized);
3640 
3641     if (g.FrameCountEnded != g.FrameCount)
3642         EndFrame();
3643     g.FrameCountRendered = g.FrameCount;
3644 
3645     // Gather ImDrawList to render (for each active window)
3646     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3647     g.DrawDataBuilder.Clear();
3648     ImGuiWindow* windows_to_render_front_most[2];
3649     windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3650     windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3651     for (int n = 0; n != g.Windows.Size; n++)
3652     {
3653         ImGuiWindow* window = g.Windows[n];
3654         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3655             AddWindowToDrawDataSelectLayer(window);
3656     }
3657     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3658         if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3659             AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]);
3660     g.DrawDataBuilder.FlattenIntoSingleLayer();
3661 
3662     // Draw software mouse cursor if requested
3663     if (g.IO.MouseDrawCursor)
3664         RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor);
3665 
3666     if (!g.OverlayDrawList.VtxBuffer.empty())
3667         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
3668 
3669     // Setup ImDrawData structure for end-user
3670     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
3671     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3672     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3673 
3674     // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
3675 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3676     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3677         g.IO.RenderDrawListsFn(&g.DrawData);
3678 #endif
3679 }
3680 
3681 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3682 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)3683 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3684 {
3685     ImGuiContext& g = *GImGui;
3686 
3687     const char* text_display_end;
3688     if (hide_text_after_double_hash)
3689         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
3690     else
3691         text_display_end = text_end;
3692 
3693     ImFont* font = g.Font;
3694     const float font_size = g.FontSize;
3695     if (text == text_display_end)
3696         return ImVec2(0.0f, font_size);
3697     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3698 
3699     // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3700     const float font_scale = font_size / font->FontSize;
3701     const float character_spacing_x = 1.0f * font_scale;
3702     if (text_size.x > 0.0f)
3703         text_size.x -= character_spacing_x;
3704     text_size.x = (float)(int)(text_size.x + 0.95f);
3705 
3706     return text_size;
3707 }
3708 
3709 // Helper to calculate coarse clipping of large list of evenly sized items.
3710 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3711 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)3712 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3713 {
3714     ImGuiContext& g = *GImGui;
3715     ImGuiWindow* window = g.CurrentWindow;
3716     if (g.LogEnabled)
3717     {
3718         // If logging is active, do not perform any clipping
3719         *out_items_display_start = 0;
3720         *out_items_display_end = items_count;
3721         return;
3722     }
3723     if (window->SkipItems)
3724     {
3725         *out_items_display_start = *out_items_display_end = 0;
3726         return;
3727     }
3728 
3729     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3730     ImRect unclipped_rect = window->ClipRect;
3731     if (g.NavMoveRequest)
3732         unclipped_rect.Add(g.NavScoringRectScreen);
3733 
3734     const ImVec2 pos = window->DC.CursorPos;
3735     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3736     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3737 
3738     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
3739     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3740         start--;
3741     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3742         end++;
3743 
3744     start = ImClamp(start, 0, items_count);
3745     end = ImClamp(end + 1, start, items_count);
3746     *out_items_display_start = start;
3747     *out_items_display_end = end;
3748 }
3749 
3750 // Find window given position, search front-to-back
3751 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3752 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3753 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()3754 static void FindHoveredWindow()
3755 {
3756     ImGuiContext& g = *GImGui;
3757 
3758     ImGuiWindow* hovered_window = NULL;
3759     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
3760         hovered_window = g.MovingWindow;
3761 
3762     ImVec2 padding_regular = g.Style.TouchExtraPadding;
3763     ImVec2 padding_for_resize_from_edges = g.IO.ConfigResizeWindowsFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
3764     for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--)
3765     {
3766         ImGuiWindow* window = g.Windows[i];
3767         if (!window->Active || window->Hidden)
3768             continue;
3769         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
3770             continue;
3771 
3772         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3773         ImRect bb(window->OuterRectClipped);
3774         if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize))
3775             bb.Expand(padding_regular);
3776         else
3777             bb.Expand(padding_for_resize_from_edges);
3778         if (!bb.Contains(g.IO.MousePos))
3779             continue;
3780 
3781         // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
3782         if (hovered_window == NULL)
3783             hovered_window = window;
3784         if (hovered_window)
3785             break;
3786     }
3787 
3788     g.HoveredWindow = hovered_window;
3789     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3790 
3791 }
3792 
3793 // Test if mouse cursor is hovering given rectangle
3794 // NB- Rectangle is clipped by our current clip setting
3795 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
IsMouseHoveringRect(const ImVec2 & r_min,const ImVec2 & r_max,bool clip)3796 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3797 {
3798     ImGuiContext& g = *GImGui;
3799 
3800     // Clip
3801     ImRect rect_clipped(r_min, r_max);
3802     if (clip)
3803         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
3804 
3805     // Expand for touch input
3806     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3807     if (!rect_for_touch.Contains(g.IO.MousePos))
3808         return false;
3809     return true;
3810 }
3811 
GetKeyIndex(ImGuiKey imgui_key)3812 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3813 {
3814     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3815     return GImGui->IO.KeyMap[imgui_key];
3816 }
3817 
3818 // Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)3819 bool ImGui::IsKeyDown(int user_key_index)
3820 {
3821     if (user_key_index < 0) return false;
3822     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3823     return GImGui->IO.KeysDown[user_key_index];
3824 }
3825 
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)3826 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3827 {
3828     if (t == 0.0f)
3829         return 1;
3830     if (t <= repeat_delay || repeat_rate <= 0.0f)
3831         return 0;
3832     const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3833     return (count > 0) ? count : 0;
3834 }
3835 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)3836 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3837 {
3838     ImGuiContext& g = *GImGui;
3839     if (key_index < 0) return false;
3840     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3841     const float t = g.IO.KeysDownDuration[key_index];
3842     return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3843 }
3844 
IsKeyPressed(int user_key_index,bool repeat)3845 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3846 {
3847     ImGuiContext& g = *GImGui;
3848     if (user_key_index < 0) return false;
3849     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3850     const float t = g.IO.KeysDownDuration[user_key_index];
3851     if (t == 0.0f)
3852         return true;
3853     if (repeat && t > g.IO.KeyRepeatDelay)
3854         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
3855     return false;
3856 }
3857 
IsKeyReleased(int user_key_index)3858 bool ImGui::IsKeyReleased(int user_key_index)
3859 {
3860     ImGuiContext& g = *GImGui;
3861     if (user_key_index < 0) return false;
3862     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3863     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
3864 }
3865 
IsMouseDown(int button)3866 bool ImGui::IsMouseDown(int button)
3867 {
3868     ImGuiContext& g = *GImGui;
3869     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3870     return g.IO.MouseDown[button];
3871 }
3872 
IsAnyMouseDown()3873 bool ImGui::IsAnyMouseDown()
3874 {
3875     ImGuiContext& g = *GImGui;
3876     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
3877         if (g.IO.MouseDown[n])
3878             return true;
3879     return false;
3880 }
3881 
IsMouseClicked(int button,bool repeat)3882 bool ImGui::IsMouseClicked(int button, bool repeat)
3883 {
3884     ImGuiContext& g = *GImGui;
3885     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3886     const float t = g.IO.MouseDownDuration[button];
3887     if (t == 0.0f)
3888         return true;
3889 
3890     if (repeat && t > g.IO.KeyRepeatDelay)
3891     {
3892         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3893         if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3894             return true;
3895     }
3896 
3897     return false;
3898 }
3899 
IsMouseReleased(int button)3900 bool ImGui::IsMouseReleased(int button)
3901 {
3902     ImGuiContext& g = *GImGui;
3903     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3904     return g.IO.MouseReleased[button];
3905 }
3906 
IsMouseDoubleClicked(int button)3907 bool ImGui::IsMouseDoubleClicked(int button)
3908 {
3909     ImGuiContext& g = *GImGui;
3910     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3911     return g.IO.MouseDoubleClicked[button];
3912 }
3913 
IsMouseDragging(int button,float lock_threshold)3914 bool ImGui::IsMouseDragging(int button, float lock_threshold)
3915 {
3916     ImGuiContext& g = *GImGui;
3917     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3918     if (!g.IO.MouseDown[button])
3919         return false;
3920     if (lock_threshold < 0.0f)
3921         lock_threshold = g.IO.MouseDragThreshold;
3922     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3923 }
3924 
GetMousePos()3925 ImVec2 ImGui::GetMousePos()
3926 {
3927     return GImGui->IO.MousePos;
3928 }
3929 
3930 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()3931 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3932 {
3933     ImGuiContext& g = *GImGui;
3934     if (g.CurrentPopupStack.Size > 0)
3935         return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos;
3936     return g.IO.MousePos;
3937 }
3938 
3939 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
IsMousePosValid(const ImVec2 * mouse_pos)3940 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
3941 {
3942     if (mouse_pos == NULL)
3943         mouse_pos = &GImGui->IO.MousePos;
3944     const float MOUSE_INVALID = -256000.0f;
3945     return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
3946 }
3947 
3948 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(int button,float lock_threshold)3949 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3950 {
3951     ImGuiContext& g = *GImGui;
3952     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3953     if (lock_threshold < 0.0f)
3954         lock_threshold = g.IO.MouseDragThreshold;
3955     if (g.IO.MouseDown[button])
3956         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3957             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
3958     return ImVec2(0.0f, 0.0f);
3959 }
3960 
ResetMouseDragDelta(int button)3961 void ImGui::ResetMouseDragDelta(int button)
3962 {
3963     ImGuiContext& g = *GImGui;
3964     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3965     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3966     g.IO.MouseClickedPos[button] = g.IO.MousePos;
3967 }
3968 
GetMouseCursor()3969 ImGuiMouseCursor ImGui::GetMouseCursor()
3970 {
3971     return GImGui->MouseCursor;
3972 }
3973 
SetMouseCursor(ImGuiMouseCursor cursor_type)3974 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3975 {
3976     GImGui->MouseCursor = cursor_type;
3977 }
3978 
CaptureKeyboardFromApp(bool capture)3979 void ImGui::CaptureKeyboardFromApp(bool capture)
3980 {
3981     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
3982 }
3983 
CaptureMouseFromApp(bool capture)3984 void ImGui::CaptureMouseFromApp(bool capture)
3985 {
3986     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
3987 }
3988 
IsItemActive()3989 bool ImGui::IsItemActive()
3990 {
3991     ImGuiContext& g = *GImGui;
3992     if (g.ActiveId)
3993     {
3994         ImGuiWindow* window = g.CurrentWindow;
3995         return g.ActiveId == window->DC.LastItemId;
3996     }
3997     return false;
3998 }
3999 
IsItemDeactivated()4000 bool ImGui::IsItemDeactivated()
4001 {
4002     ImGuiContext& g = *GImGui;
4003     ImGuiWindow* window = g.CurrentWindow;
4004     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4005 }
4006 
IsItemDeactivatedAfterEdit()4007 bool ImGui::IsItemDeactivatedAfterEdit()
4008 {
4009     ImGuiContext& g = *GImGui;
4010     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
4011 }
4012 
IsItemFocused()4013 bool ImGui::IsItemFocused()
4014 {
4015     ImGuiContext& g = *GImGui;
4016     return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
4017 }
4018 
IsItemClicked(int mouse_button)4019 bool ImGui::IsItemClicked(int mouse_button)
4020 {
4021     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4022 }
4023 
IsAnyItemHovered()4024 bool ImGui::IsAnyItemHovered()
4025 {
4026     ImGuiContext& g = *GImGui;
4027     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4028 }
4029 
IsAnyItemActive()4030 bool ImGui::IsAnyItemActive()
4031 {
4032     ImGuiContext& g = *GImGui;
4033     return g.ActiveId != 0;
4034 }
4035 
IsAnyItemFocused()4036 bool ImGui::IsAnyItemFocused()
4037 {
4038     ImGuiContext& g = *GImGui;
4039     return g.NavId != 0 && !g.NavDisableHighlight;
4040 }
4041 
IsItemVisible()4042 bool ImGui::IsItemVisible()
4043 {
4044     ImGuiWindow* window = GetCurrentWindowRead();
4045     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4046 }
4047 
IsItemEdited()4048 bool ImGui::IsItemEdited()
4049 {
4050     ImGuiWindow* window = GetCurrentWindowRead();
4051     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4052 }
4053 
4054 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()4055 void ImGui::SetItemAllowOverlap()
4056 {
4057     ImGuiContext& g = *GImGui;
4058     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4059         g.HoveredIdAllowOverlap = true;
4060     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4061         g.ActiveIdAllowOverlap = true;
4062 }
4063 
GetItemRectMin()4064 ImVec2 ImGui::GetItemRectMin()
4065 {
4066     ImGuiWindow* window = GetCurrentWindowRead();
4067     return window->DC.LastItemRect.Min;
4068 }
4069 
GetItemRectMax()4070 ImVec2 ImGui::GetItemRectMax()
4071 {
4072     ImGuiWindow* window = GetCurrentWindowRead();
4073     return window->DC.LastItemRect.Max;
4074 }
4075 
GetItemRectSize()4076 ImVec2 ImGui::GetItemRectSize()
4077 {
4078     ImGuiWindow* window = GetCurrentWindowRead();
4079     return window->DC.LastItemRect.GetSize();
4080 }
4081 
GetViewportRect()4082 static ImRect GetViewportRect()
4083 {
4084     ImGuiContext& g = *GImGui;
4085     if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4086         return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4087     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4088 }
4089 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4090 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4091 {
4092     ImGuiContext& g = *GImGui;
4093     ImGuiWindow* parent_window = g.CurrentWindow;
4094 
4095     flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4096     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4097 
4098     // Size
4099     const ImVec2 content_avail = GetContentRegionAvail();
4100     ImVec2 size = ImFloor(size_arg);
4101     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4102     if (size.x <= 0.0f)
4103         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4104     if (size.y <= 0.0f)
4105         size.y = ImMax(content_avail.y + size.y, 4.0f);
4106     SetNextWindowSize(size);
4107 
4108     // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
4109     char title[256];
4110     if (name)
4111         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4112     else
4113         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4114 
4115     const float backup_border_size = g.Style.ChildBorderSize;
4116     if (!border)
4117         g.Style.ChildBorderSize = 0.0f;
4118     bool ret = Begin(title, NULL, flags);
4119     g.Style.ChildBorderSize = backup_border_size;
4120 
4121     ImGuiWindow* child_window = g.CurrentWindow;
4122     child_window->ChildId = id;
4123     child_window->AutoFitChildAxises = auto_fit_axises;
4124 
4125     // Process navigation-in immediately so NavInit can run on first frame
4126     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4127     {
4128         FocusWindow(child_window);
4129         NavInitWindow(child_window, false);
4130         SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4131         g.ActiveIdSource = ImGuiInputSource_Nav;
4132     }
4133     return ret;
4134 }
4135 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4136 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4137 {
4138     ImGuiWindow* window = GetCurrentWindow();
4139     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4140 }
4141 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4142 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4143 {
4144     IM_ASSERT(id != 0);
4145     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4146 }
4147 
EndChild()4148 void ImGui::EndChild()
4149 {
4150     ImGuiContext& g = *GImGui;
4151     ImGuiWindow* window = g.CurrentWindow;
4152 
4153     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
4154     if (window->BeginCount > 1)
4155     {
4156         End();
4157     }
4158     else
4159     {
4160         ImVec2 sz = window->Size;
4161         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4162             sz.x = ImMax(4.0f, sz.x);
4163         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4164             sz.y = ImMax(4.0f, sz.y);
4165         End();
4166 
4167         ImGuiWindow* parent_window = g.CurrentWindow;
4168         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4169         ItemSize(sz);
4170         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4171         {
4172             ItemAdd(bb, window->ChildId);
4173             RenderNavHighlight(bb, window->ChildId);
4174 
4175             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4176             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4177                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4178         }
4179         else
4180         {
4181             // Not navigable into
4182             ItemAdd(bb, 0);
4183         }
4184     }
4185 }
4186 
4187 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4188 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4189 {
4190     ImGuiContext& g = *GImGui;
4191     const ImGuiStyle& style = g.Style;
4192     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4193     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4194     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4195     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4196     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4197     PopStyleVar(3);
4198     PopStyleColor();
4199     return ret;
4200 }
4201 
EndChildFrame()4202 void ImGui::EndChildFrame()
4203 {
4204     EndChild();
4205 }
4206 
4207 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4208 static void CheckStacksSize(ImGuiWindow* window, bool write)
4209 {
4210     // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
4211     ImGuiContext& g = *GImGui;
4212     int* p_backup = &window->DC.StackSizesBackup[0];
4213     { int current = window->IDStack.Size;       if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");   p_backup++; }    // Too few or too many PopID()/TreePop()
4214     { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");                p_backup++; }    // Too few or too many EndGroup()
4215     { int current = g.CurrentPopupStack.Size;   if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4216     // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
4217     { int current = g.ColorModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!");       p_backup++; }    // Too few or too many PopStyleColor()
4218     { int current = g.StyleModifiers.Size;      if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!");           p_backup++; }    // Too few or too many PopStyleVar()
4219     { int current = g.FontStack.Size;           if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!");                   p_backup++; }    // Too few or too many PopFont()
4220     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4221 }
4222 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4223 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4224 {
4225     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4226     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4227     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4228 }
4229 
FindWindowByID(ImGuiID id)4230 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4231 {
4232     ImGuiContext& g = *GImGui;
4233     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4234 }
4235 
FindWindowByName(const char * name)4236 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4237 {
4238     ImGuiID id = ImHash(name, 0);
4239     return FindWindowByID(id);
4240 }
4241 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4242 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4243 {
4244     ImGuiContext& g = *GImGui;
4245 
4246     // Create window the first time
4247     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4248     window->Flags = flags;
4249     g.WindowsById.SetVoidPtr(window->ID, window);
4250 
4251     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4252     window->Pos = ImVec2(60, 60);
4253 
4254     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4255     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4256         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4257         {
4258             // Retrieve settings from .ini file
4259             window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
4260             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4261             window->Pos = ImFloor(settings->Pos);
4262             window->Collapsed = settings->Collapsed;
4263             if (ImLengthSqr(settings->Size) > 0.00001f)
4264                 size = ImFloor(settings->Size);
4265         }
4266     window->Size = window->SizeFull = window->SizeFullAtLastBegin = ImFloor(size);
4267     window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4268 
4269     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4270     {
4271         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4272         window->AutoFitOnlyGrows = false;
4273     }
4274     else
4275     {
4276         if (window->Size.x <= 0.0f)
4277             window->AutoFitFramesX = 2;
4278         if (window->Size.y <= 0.0f)
4279             window->AutoFitFramesY = 2;
4280         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4281     }
4282 
4283     g.WindowsFocusOrder.push_back(window);
4284     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4285         g.Windows.push_front(window); // Quite slow but rare and only once
4286     else
4287         g.Windows.push_back(window);
4288     return window;
4289 }
4290 
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4291 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4292 {
4293     ImGuiContext& g = *GImGui;
4294     if (g.NextWindowData.SizeConstraintCond != 0)
4295     {
4296         // Using -1,-1 on either X/Y axis to preserve the current size.
4297         ImRect cr = g.NextWindowData.SizeConstraintRect;
4298         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4299         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4300         if (g.NextWindowData.SizeCallback)
4301         {
4302             ImGuiSizeCallbackData data;
4303             data.UserData = g.NextWindowData.SizeCallbackUserData;
4304             data.Pos = window->Pos;
4305             data.CurrentSize = window->SizeFull;
4306             data.DesiredSize = new_size;
4307             g.NextWindowData.SizeCallback(&data);
4308             new_size = data.DesiredSize;
4309         }
4310     }
4311 
4312     // Minimum size
4313     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4314     {
4315         new_size = ImMax(new_size, g.Style.WindowMinSize);
4316         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
4317     }
4318     return new_size;
4319 }
4320 
CalcSizeContents(ImGuiWindow * window)4321 static ImVec2 CalcSizeContents(ImGuiWindow* window)
4322 {
4323     ImVec2 sz;
4324     sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4325     sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4326     return sz + window->WindowPadding;
4327 }
4328 
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)4329 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4330 {
4331     ImGuiContext& g = *GImGui;
4332     ImGuiStyle& style = g.Style;
4333     if (window->Flags & ImGuiWindowFlags_Tooltip)
4334     {
4335         // Tooltip always resize
4336         return size_contents;
4337     }
4338     else
4339     {
4340         // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4341         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4342         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4343         ImVec2 size_min = style.WindowMinSize;
4344         if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
4345             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4346         ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4347         ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
4348         if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4349             size_auto_fit.y += style.ScrollbarSize;
4350         if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4351             size_auto_fit.x += style.ScrollbarSize;
4352         return size_auto_fit;
4353     }
4354 }
4355 
CalcWindowExpectedSize(ImGuiWindow * window)4356 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4357 {
4358     ImVec2 size_contents = CalcSizeContents(window);
4359     return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents));
4360 }
4361 
GetWindowScrollMaxX(ImGuiWindow * window)4362 float ImGui::GetWindowScrollMaxX(ImGuiWindow* window)
4363 {
4364     return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4365 }
4366 
GetWindowScrollMaxY(ImGuiWindow * window)4367 float ImGui::GetWindowScrollMaxY(ImGuiWindow* window)
4368 {
4369     return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4370 }
4371 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)4372 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4373 {
4374     ImGuiContext& g = *GImGui;
4375     ImVec2 scroll = window->Scroll;
4376     if (window->ScrollTarget.x < FLT_MAX)
4377     {
4378         float cr_x = window->ScrollTargetCenterRatio.x;
4379         scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4380     }
4381     if (window->ScrollTarget.y < FLT_MAX)
4382     {
4383         // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
4384         float cr_y = window->ScrollTargetCenterRatio.y;
4385         float target_y = window->ScrollTarget.y;
4386         if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4387             target_y = 0.0f;
4388         if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4389             target_y = window->SizeContents.y;
4390         scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4391     }
4392     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4393     if (!window->Collapsed && !window->SkipItems)
4394     {
4395         scroll.x = ImMin(scroll.x, ImGui::GetWindowScrollMaxX(window));
4396         scroll.y = ImMin(scroll.y, ImGui::GetWindowScrollMaxY(window));
4397     }
4398     return scroll;
4399 }
4400 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4401 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4402 {
4403     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4404         return ImGuiCol_PopupBg;
4405     if (flags & ImGuiWindowFlags_ChildWindow)
4406         return ImGuiCol_ChildBg;
4407     return ImGuiCol_WindowBg;
4408 }
4409 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4410 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4411 {
4412     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
4413     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4414     ImVec2 size_expected = pos_max - pos_min;
4415     ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
4416     *out_pos = pos_min;
4417     if (corner_norm.x == 0.0f)
4418         out_pos->x -= (size_constrained.x - size_expected.x);
4419     if (corner_norm.y == 0.0f)
4420         out_pos->y -= (size_constrained.y - size_expected.y);
4421     *out_size = size_constrained;
4422 }
4423 
4424 struct ImGuiResizeGripDef
4425 {
4426     ImVec2  CornerPos;
4427     ImVec2  InnerDir;
4428     int     AngleMin12, AngleMax12;
4429 };
4430 
4431 const ImGuiResizeGripDef resize_grip_def[4] =
4432 {
4433     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4434     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4435     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4436     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4437 };
4438 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4439 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4440 {
4441     ImRect rect = window->Rect();
4442     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4443     if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);
4444     if (border_n == 1) return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding);
4445     if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);
4446     if (border_n == 3) return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding);
4447     IM_ASSERT(0);
4448     return ImRect();
4449 }
4450 
4451 // Handle resize for: Resize Grips, Borders, Gamepad
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])4452 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4453 {
4454     ImGuiContext& g = *GImGui;
4455     ImGuiWindowFlags flags = window->Flags;
4456     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4457         return;
4458     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit Debug window.
4459         return;
4460 
4461     const int resize_border_count = g.IO.ConfigResizeWindowsFromEdges ? 4 : 0;
4462     const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4463     const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f);
4464     const float grip_hover_outer_size = g.IO.ConfigResizeWindowsFromEdges ? RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS : 0.0f;
4465 
4466     ImVec2 pos_target(FLT_MAX, FLT_MAX);
4467     ImVec2 size_target(FLT_MAX, FLT_MAX);
4468 
4469     // Manual resize grips
4470     PushID("#RESIZE");
4471     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4472     {
4473         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4474         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4475 
4476         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4477         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4478         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4479         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4480         bool hovered, held;
4481         ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4482         //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4483         if (hovered || held)
4484             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4485 
4486         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4487         {
4488             // Manual auto-fit when double-clicking
4489             size_target = CalcSizeAfterConstraint(window, size_auto_fit);
4490             ClearActiveID();
4491         }
4492         else if (held)
4493         {
4494             // Resize from any of the four corners
4495             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4496             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPos); // Corner of the window corresponding to our corner grip
4497             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
4498         }
4499         if (resize_grip_n == 0 || held || hovered)
4500             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4501     }
4502     for (int border_n = 0; border_n < resize_border_count; border_n++)
4503     {
4504         bool hovered, held;
4505         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS);
4506         ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4507         //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4508         if ((hovered && g.HoveredIdTimer > RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER) || held)
4509         {
4510             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4511             if (held) *border_held = border_n;
4512         }
4513         if (held)
4514         {
4515             ImVec2 border_target = window->Pos;
4516             ImVec2 border_posn;
4517             if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4518             if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4519             if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4520             if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); }
4521             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4522         }
4523     }
4524     PopID();
4525 
4526     // Navigation resize (keyboard/gamepad)
4527     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4528     {
4529         ImVec2 nav_resize_delta;
4530         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4531             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4532         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4533             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4534         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4535         {
4536             const float NAV_RESIZE_SPEED = 600.0f;
4537             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4538             g.NavWindowingToggleLayer = false;
4539             g.NavDisableMouseHover = true;
4540             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4541             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4542             size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4543         }
4544     }
4545 
4546     // Apply back modified position/size to window
4547     if (size_target.x != FLT_MAX)
4548     {
4549         window->SizeFull = size_target;
4550         MarkIniSettingsDirty(window);
4551     }
4552     if (pos_target.x != FLT_MAX)
4553     {
4554         window->Pos = ImFloor(pos_target);
4555         MarkIniSettingsDirty(window);
4556     }
4557 
4558     window->Size = window->SizeFull;
4559 }
4560 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)4561 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4562 {
4563     window->ParentWindow = parent_window;
4564     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4565     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4566         window->RootWindow = parent_window->RootWindow;
4567     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4568         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4569     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4570         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4571 }
4572 
4573 // Push a new ImGui window to add widgets to.
4574 // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
4575 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4576 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4577 //   You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
4578 // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
4579 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)4580 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4581 {
4582     ImGuiContext& g = *GImGui;
4583     const ImGuiStyle& style = g.Style;
4584     IM_ASSERT(name != NULL);                        // Window name required
4585     IM_ASSERT(g.FrameScopeActive);                  // Forgot to call ImGui::NewFrame()
4586     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4587 
4588     // Find or create
4589     ImGuiWindow* window = FindWindowByName(name);
4590     const bool window_just_created = (window == NULL);
4591     if (window_just_created)
4592     {
4593         ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
4594         window = CreateNewWindow(name, size_on_first_use, flags);
4595     }
4596 
4597     // Automatically disable manual moving/resizing when NoInputs is set
4598     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
4599         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4600 
4601     if (flags & ImGuiWindowFlags_NavFlattened)
4602         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4603 
4604     const int current_frame = g.FrameCount;
4605     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4606     if (first_begin_of_the_frame)
4607         window->Flags = (ImGuiWindowFlags)flags;
4608     else
4609         flags = window->Flags;
4610 
4611     // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
4612     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4613     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4614     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4615     window->HasCloseButton = (p_open != NULL);
4616 
4617     // Update the Appearing flag
4618     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4619     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4620     if (flags & ImGuiWindowFlags_Popup)
4621     {
4622         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4623         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4624         window_just_activated_by_user |= (window != popup_ref.Window);
4625     }
4626     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4627     if (window->Appearing)
4628         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4629 
4630     // Add to stack
4631     g.CurrentWindowStack.push_back(window);
4632     SetCurrentWindow(window);
4633     CheckStacksSize(window, true);
4634     if (flags & ImGuiWindowFlags_Popup)
4635     {
4636         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4637         popup_ref.Window = window;
4638         g.CurrentPopupStack.push_back(popup_ref);
4639         window->PopupId = popup_ref.PopupId;
4640     }
4641 
4642     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4643         window->NavLastIds[0] = 0;
4644 
4645     // Process SetNextWindow***() calls
4646     bool window_pos_set_by_api = false;
4647     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4648     if (g.NextWindowData.PosCond)
4649     {
4650         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4651         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
4652         {
4653             // May be processed on the next frame if this is our first frame and we are measuring size
4654             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4655             window->SetWindowPosVal = g.NextWindowData.PosVal;
4656             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4657             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4658         }
4659         else
4660         {
4661             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
4662         }
4663     }
4664     if (g.NextWindowData.SizeCond)
4665     {
4666         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4667         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4668         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
4669     }
4670     if (g.NextWindowData.ContentSizeCond)
4671     {
4672         // Adjust passed "client size" to become a "window size"
4673         window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4674         if (window->SizeContentsExplicit.y != 0.0f)
4675             window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4676     }
4677     else if (first_begin_of_the_frame)
4678     {
4679         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4680     }
4681     if (g.NextWindowData.CollapsedCond)
4682         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
4683     if (g.NextWindowData.FocusCond)
4684         FocusWindow(window);
4685     if (window->Appearing)
4686         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4687 
4688     // When reusing window again multiple times a frame, just append content (don't need to setup again)
4689     if (first_begin_of_the_frame)
4690     {
4691         // Initialize
4692         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4693         UpdateWindowParentAndRootLinks(window, flags, parent_window);
4694 
4695         window->Active = true;
4696         window->BeginOrderWithinParent = 0;
4697         window->BeginOrderWithinContext = g.WindowsActiveCount++;
4698         window->BeginCount = 0;
4699         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4700         window->LastFrameActive = current_frame;
4701         window->IDStack.resize(1);
4702 
4703         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
4704 
4705         // Update contents size from last frame for auto-fitting (or use explicit size)
4706         window->SizeContents = CalcSizeContents(window);
4707         if (window->HiddenFramesRegular > 0)
4708             window->HiddenFramesRegular--;
4709         if (window->HiddenFramesForResize > 0)
4710             window->HiddenFramesForResize--;
4711 
4712         // Hide new windows for one frame until they calculate their size
4713         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
4714             window->HiddenFramesForResize = 1;
4715 
4716         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
4717         // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
4718         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
4719         {
4720             window->HiddenFramesForResize = 1;
4721             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4722             {
4723                 if (!window_size_x_set_by_api)
4724                     window->Size.x = window->SizeFull.x = 0.f;
4725                 if (!window_size_y_set_by_api)
4726                     window->Size.y = window->SizeFull.y = 0.f;
4727                 window->SizeContents = ImVec2(0.f, 0.f);
4728             }
4729         }
4730 
4731         SetCurrentWindow(window);
4732 
4733         // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
4734         window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4735         window->WindowPadding = style.WindowPadding;
4736         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4737             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4738         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
4739         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
4740 
4741         // Collapse window by double-clicking on title bar
4742         // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
4743         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4744         {
4745             // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
4746             ImRect title_bar_rect = window->TitleBarRect();
4747             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4748                 window->WantCollapseToggle = true;
4749             if (window->WantCollapseToggle)
4750             {
4751                 window->Collapsed = !window->Collapsed;
4752                 MarkIniSettingsDirty(window);
4753                 FocusWindow(window);
4754             }
4755         }
4756         else
4757         {
4758             window->Collapsed = false;
4759         }
4760         window->WantCollapseToggle = false;
4761 
4762         // SIZE
4763 
4764         // Calculate auto-fit size, handle automatic resize
4765         const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
4766         ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
4767         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
4768         {
4769             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
4770             if (!window_size_x_set_by_api)
4771                 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
4772             if (!window_size_y_set_by_api)
4773                 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
4774         }
4775         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4776         {
4777             // Auto-fit may only grow window during the first few frames
4778             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4779             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
4780                 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4781             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
4782                 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4783             if (!window->Collapsed)
4784                 MarkIniSettingsDirty(window);
4785         }
4786 
4787         // Apply minimum/maximum window size constraints and final size
4788         window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
4789         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
4790 
4791         // SCROLLBAR STATUS
4792 
4793         // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
4794         if (!window->Collapsed)
4795         {
4796             // When reading the current size we need to read it after size constraints have been applied
4797             float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
4798             float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
4799             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
4800             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4801             if (window->ScrollbarX && !window->ScrollbarY)
4802                 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4803             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4804         }
4805 
4806         // POSITION
4807 
4808         // Popup latch its initial position, will position itself when it appears next frame
4809         if (window_just_activated_by_user)
4810         {
4811             window->AutoPosLastDirection = ImGuiDir_None;
4812             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4813                 window->Pos = g.CurrentPopupStack.back().OpenPopupPos;
4814         }
4815 
4816         // Position child window
4817         if (flags & ImGuiWindowFlags_ChildWindow)
4818         {
4819             window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
4820             parent_window->DC.ChildWindows.push_back(window);
4821             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
4822                 window->Pos = parent_window->DC.CursorPos;
4823         }
4824 
4825         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
4826         if (window_pos_with_pivot)
4827             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
4828         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
4829             window->Pos = FindBestWindowPosForPopup(window);
4830         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
4831             window->Pos = FindBestWindowPosForPopup(window);
4832         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
4833             window->Pos = FindBestWindowPosForPopup(window);
4834 
4835         // Clamp position so it stays visible
4836         if (!(flags & ImGuiWindowFlags_ChildWindow))
4837         {
4838             if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
4839             {
4840                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
4841                 window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size;
4842                 window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
4843             }
4844         }
4845         window->Pos = ImFloor(window->Pos);
4846 
4847         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
4848         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
4849 
4850         // Prepare for item focus requests
4851         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4852         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4853         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4854         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4855 
4856         // Apply scrolling
4857         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
4858         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4859 
4860         // Apply window focus (new and reactivated windows are moved to front)
4861         bool want_focus = false;
4862         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4863             if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4864                 want_focus = true;
4865 
4866         // Handle manual resize: Resize Grips, Borders, Gamepad
4867         int border_held = -1;
4868         ImU32 resize_grip_col[4] = { 0 };
4869         const int resize_grip_count = g.IO.ConfigResizeWindowsFromEdges ? 2 : 1; // 4
4870         const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4871         if (!window->Collapsed)
4872             UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
4873 
4874         // Default item width. Make it proportional to window size if window manually resizes
4875         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4876             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4877         else
4878             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4879 
4880         // DRAWING
4881 
4882         // Setup draw list and outer clipping rectangle
4883         window->DrawList->Clear();
4884         window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
4885         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
4886         ImRect viewport_rect(GetViewportRect());
4887         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
4888             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
4889         else
4890             PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
4891 
4892         // Draw modal window background (darkens what is behind them, all viewports)
4893         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
4894         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
4895         if (dim_bg_for_modal || dim_bg_for_window_list)
4896         {
4897             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4898             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
4899         }
4900 
4901         // Draw navigation selection/windowing rectangle background
4902         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
4903         {
4904             ImRect bb = window->Rect();
4905             bb.Expand(g.FontSize);
4906             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
4907                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
4908         }
4909 
4910         // Draw window + handle manual resize
4911         const float window_rounding = window->WindowRounding;
4912         const float window_border_size = window->WindowBorderSize;
4913         const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
4914         const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
4915         const ImRect title_bar_rect = window->TitleBarRect();
4916         if (window->Collapsed)
4917         {
4918             // Title bar only
4919             float backup_border_size = style.FrameBorderSize;
4920             g.Style.FrameBorderSize = window->WindowBorderSize;
4921             ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
4922             RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
4923             g.Style.FrameBorderSize = backup_border_size;
4924         }
4925         else
4926         {
4927             // Window background
4928             if (!(flags & ImGuiWindowFlags_NoBackground))
4929             {
4930                 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
4931                 if (g.NextWindowData.BgAlphaCond != 0)
4932                     bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
4933                 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
4934             }
4935             g.NextWindowData.BgAlphaCond = 0;
4936 
4937             // Title bar
4938             ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
4939             if (!(flags & ImGuiWindowFlags_NoTitleBar))
4940                 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
4941 
4942             // Menu bar
4943             if (flags & ImGuiWindowFlags_MenuBar)
4944             {
4945                 ImRect menu_bar_rect = window->MenuBarRect();
4946                 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.
4947                 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
4948                 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
4949                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4950             }
4951 
4952             // Scrollbars
4953             if (window->ScrollbarX)
4954                 Scrollbar(ImGuiLayoutType_Horizontal);
4955             if (window->ScrollbarY)
4956                 Scrollbar(ImGuiLayoutType_Vertical);
4957 
4958             // Render resize grips (after their input handling so we don't have a frame of latency)
4959             if (!(flags & ImGuiWindowFlags_NoResize))
4960             {
4961                 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4962                 {
4963                     const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4964                     const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4965                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
4966                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
4967                     window->DrawList->PathArcToFast(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);
4968                     window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
4969                 }
4970             }
4971 
4972             // Borders
4973             if (window_border_size > 0.0f && !(flags & ImGuiWindowFlags_NoBackground))
4974                 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
4975             if (border_held != -1)
4976             {
4977                 ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f);
4978                 window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
4979             }
4980             if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
4981                 window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4982         }
4983 
4984         // Draw navigation selection/windowing rectangle border
4985         if (g.NavWindowingTargetAnim == window)
4986         {
4987             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
4988             ImRect bb = window->Rect();
4989             bb.Expand(g.FontSize);
4990             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
4991             {
4992                 bb.Expand(-g.FontSize - 1.0f);
4993                 rounding = window->WindowRounding;
4994             }
4995             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
4996         }
4997 
4998         // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
4999         window->SizeFullAtLastBegin = window->SizeFull;
5000 
5001         // Update various regions. Variables they depends on are set above in this function.
5002         // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5003         window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5004         window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
5005         window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
5006         window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
5007 
5008         // Setup drawing context
5009         // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
5010         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5011         window->DC.GroupOffset.x = 0.0f;
5012         window->DC.ColumnsOffset.x = 0.0f;
5013         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
5014         window->DC.CursorPos = window->DC.CursorStartPos;
5015         window->DC.CursorPosPrevLine = window->DC.CursorPos;
5016         window->DC.CursorMaxPos = window->DC.CursorStartPos;
5017         window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5018         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5019         window->DC.NavHideHighlightOneFrame = false;
5020         window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f);
5021         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5022         window->DC.NavLayerActiveMaskNext = 0x00;
5023         window->DC.MenuBarAppending = false;
5024         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
5025         window->DC.ChildWindows.resize(0);
5026         window->DC.LayoutType = ImGuiLayoutType_Vertical;
5027         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5028         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5029         window->DC.ItemWidth = window->ItemWidthDefault;
5030         window->DC.TextWrapPos = -1.0f; // disabled
5031         window->DC.ItemFlagsStack.resize(0);
5032         window->DC.ItemWidthStack.resize(0);
5033         window->DC.TextWrapPosStack.resize(0);
5034         window->DC.ColumnsSet = NULL;
5035         window->DC.TreeDepth = 0;
5036         window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
5037         window->DC.StateStorage = &window->StateStorage;
5038         window->DC.GroupStack.resize(0);
5039         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5040 
5041         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5042         {
5043             window->DC.ItemFlags = parent_window->DC.ItemFlags;
5044             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5045         }
5046 
5047         if (window->AutoFitFramesX > 0)
5048             window->AutoFitFramesX--;
5049         if (window->AutoFitFramesY > 0)
5050             window->AutoFitFramesY--;
5051 
5052         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5053         if (want_focus)
5054         {
5055             FocusWindow(window);
5056             NavInitWindow(window, false);
5057         }
5058 
5059         // Title bar
5060         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5061         {
5062             // Close & collapse button are on layer 1 (same as menus) and don't default focus
5063             const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5064             window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5065             window->DC.NavLayerCurrent++;
5066             window->DC.NavLayerCurrentMask <<= 1;
5067 
5068             // Collapse button
5069             if (!(flags & ImGuiWindowFlags_NoCollapse))
5070                 if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos))
5071                     window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
5072 
5073             // Close button
5074             if (p_open != NULL)
5075             {
5076                 const float pad = style.FramePadding.y;
5077                 const float rad = g.FontSize * 0.5f;
5078                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
5079                     *p_open = false;
5080             }
5081 
5082             window->DC.NavLayerCurrent--;
5083             window->DC.NavLayerCurrentMask >>= 1;
5084             window->DC.ItemFlags = item_flags_backup;
5085 
5086             // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.)
5087             ImVec2 text_size = CalcTextSize(name, NULL, true);
5088             ImRect text_r = title_bar_rect;
5089             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5090             float pad_right = (p_open == NULL)                     ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5091             if (style.WindowTitleAlign.x > 0.0f)
5092                 pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
5093             text_r.Min.x += pad_left;
5094             text_r.Max.x -= pad_right;
5095             ImRect clip_rect = text_r;
5096             clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton()
5097             RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
5098         }
5099 
5100         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5101         window->OuterRectClipped = window->Rect();
5102         window->OuterRectClipped.ClipWith(window->ClipRect);
5103 
5104         // Pressing CTRL+C while holding on a window copy its content to the clipboard
5105         // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
5106         // Maybe we can support CTRL+C on every element?
5107         /*
5108         if (g.ActiveId == move_id)
5109             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5110                 LogToClipboard();
5111         */
5112 
5113         // Inner rectangle
5114         // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5115         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5116         window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5117         window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5118         window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5119         window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5120         //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5121 
5122         // Inner clipping rectangle
5123         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5124         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5125         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
5126         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5127         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
5128 
5129         // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.).
5130         window->DC.LastItemId = window->MoveId;
5131         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5132         window->DC.LastItemRect = title_bar_rect;
5133     }
5134 
5135     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5136 
5137     // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
5138     if (first_begin_of_the_frame)
5139         window->WriteAccessed = false;
5140 
5141     window->BeginCount++;
5142     g.NextWindowData.Clear();
5143 
5144     if (flags & ImGuiWindowFlags_ChildWindow)
5145     {
5146         // Child window can be out of sight and have "negative" clip windows.
5147         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5148         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5149 
5150         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5151             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5152                 window->HiddenFramesRegular = 1;
5153 
5154         // Completely hide along with parent or if parent is collapsed
5155         if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5156             window->HiddenFramesRegular = 1;
5157     }
5158 
5159     // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)
5160     if (style.Alpha <= 0.0f)
5161         window->HiddenFramesRegular = 1;
5162 
5163     // Update the Hidden flag
5164     window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize);
5165 
5166     // Return false if we don't intend to display anything to allow user to perform an early out optimization
5167     window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5168 
5169     return !window->SkipItems;
5170 }
5171 
5172 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5173 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)5174 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5175 {
5176     // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file.
5177     if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5178         SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
5179 
5180     // Old API feature: override the window background alpha with a parameter.
5181     if (bg_alpha_override >= 0.0f)
5182         SetNextWindowBgAlpha(bg_alpha_override);
5183 
5184     return Begin(name, p_open, flags);
5185 }
5186 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5187 
End()5188 void ImGui::End()
5189 {
5190     ImGuiContext& g = *GImGui;
5191     ImGuiWindow* window = g.CurrentWindow;
5192 
5193     if (window->DC.ColumnsSet != NULL)
5194         EndColumns();
5195     PopClipRect();   // Inner window clip rectangle
5196 
5197     // Stop logging
5198     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
5199         LogFinish();
5200 
5201     // Pop from window stack
5202     g.CurrentWindowStack.pop_back();
5203     if (window->Flags & ImGuiWindowFlags_Popup)
5204         g.CurrentPopupStack.pop_back();
5205     CheckStacksSize(window, false);
5206     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5207 }
5208 
BringWindowToFocusFront(ImGuiWindow * window)5209 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5210 {
5211     ImGuiContext& g = *GImGui;
5212     if (g.WindowsFocusOrder.back() == window)
5213         return;
5214     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window
5215         if (g.WindowsFocusOrder[i] == window)
5216         {
5217             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5218             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5219             break;
5220         }
5221 }
5222 
BringWindowToDisplayFront(ImGuiWindow * window)5223 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5224 {
5225     ImGuiContext& g = *GImGui;
5226     ImGuiWindow* current_front_window = g.Windows.back();
5227     if (current_front_window == window || current_front_window->RootWindow == window)
5228         return;
5229     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5230         if (g.Windows[i] == window)
5231         {
5232             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5233             g.Windows[g.Windows.Size - 1] = window;
5234             break;
5235         }
5236 }
5237 
BringWindowToDisplayBack(ImGuiWindow * window)5238 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5239 {
5240     ImGuiContext& g = *GImGui;
5241     if (g.Windows[0] == window)
5242         return;
5243     for (int i = 0; i < g.Windows.Size; i++)
5244         if (g.Windows[i] == window)
5245         {
5246             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5247             g.Windows[0] = window;
5248             break;
5249         }
5250 }
5251 
5252 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5253 void ImGui::FocusWindow(ImGuiWindow* window)
5254 {
5255     ImGuiContext& g = *GImGui;
5256 
5257     if (g.NavWindow != window)
5258     {
5259         g.NavWindow = window;
5260         if (window && g.NavDisableMouseHover)
5261             g.NavMousePosDirty = true;
5262         g.NavInitRequest = false;
5263         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5264         g.NavIdIsAlive = false;
5265         g.NavLayer = 0;
5266         //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
5267     }
5268 
5269     // Passing NULL allow to disable keyboard focus
5270     if (!window)
5271         return;
5272 
5273     // Move the root window to the top of the pile
5274     if (window->RootWindow)
5275         window = window->RootWindow;
5276 
5277     // Steal focus on active widgets
5278     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5279         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5280             ClearActiveID();
5281 
5282     // Bring to front
5283     BringWindowToFocusFront(window);
5284     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5285         BringWindowToDisplayFront(window);
5286 }
5287 
FocusPreviousWindowIgnoringOne(ImGuiWindow * ignore_window)5288 void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
5289 {
5290     ImGuiContext& g = *GImGui;
5291     for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
5292     {
5293         // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
5294         ImGuiWindow* window = g.WindowsFocusOrder[i];
5295         if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5296             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5297             {
5298                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5299                 FocusWindow(focus_window);
5300                 return;
5301             }
5302     }
5303 }
5304 
PushItemWidth(float item_width)5305 void ImGui::PushItemWidth(float item_width)
5306 {
5307     ImGuiWindow* window = GetCurrentWindow();
5308     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5309     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5310 }
5311 
PushMultiItemsWidths(int components,float w_full)5312 void ImGui::PushMultiItemsWidths(int components, float w_full)
5313 {
5314     ImGuiWindow* window = GetCurrentWindow();
5315     const ImGuiStyle& style = GImGui->Style;
5316     if (w_full <= 0.0f)
5317         w_full = CalcItemWidth();
5318     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5319     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5320     window->DC.ItemWidthStack.push_back(w_item_last);
5321     for (int i = 0; i < components-1; i++)
5322         window->DC.ItemWidthStack.push_back(w_item_one);
5323     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5324 }
5325 
PopItemWidth()5326 void ImGui::PopItemWidth()
5327 {
5328     ImGuiWindow* window = GetCurrentWindow();
5329     window->DC.ItemWidthStack.pop_back();
5330     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5331 }
5332 
CalcItemWidth()5333 float ImGui::CalcItemWidth()
5334 {
5335     ImGuiWindow* window = GetCurrentWindowRead();
5336     float w = window->DC.ItemWidth;
5337     if (w < 0.0f)
5338     {
5339         // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
5340         float width_to_right_edge = GetContentRegionAvail().x;
5341         w = ImMax(1.0f, width_to_right_edge + w);
5342     }
5343     w = (float)(int)w;
5344     return w;
5345 }
5346 
SetCurrentFont(ImFont * font)5347 void ImGui::SetCurrentFont(ImFont* font)
5348 {
5349     ImGuiContext& g = *GImGui;
5350     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5351     IM_ASSERT(font->Scale > 0.0f);
5352     g.Font = font;
5353     g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
5354     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5355 
5356     ImFontAtlas* atlas = g.Font->ContainerAtlas;
5357     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5358     g.DrawListSharedData.Font = g.Font;
5359     g.DrawListSharedData.FontSize = g.FontSize;
5360 }
5361 
PushFont(ImFont * font)5362 void ImGui::PushFont(ImFont* font)
5363 {
5364     ImGuiContext& g = *GImGui;
5365     if (!font)
5366         font = GetDefaultFont();
5367     SetCurrentFont(font);
5368     g.FontStack.push_back(font);
5369     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
5370 }
5371 
PopFont()5372 void  ImGui::PopFont()
5373 {
5374     ImGuiContext& g = *GImGui;
5375     g.CurrentWindow->DrawList->PopTextureID();
5376     g.FontStack.pop_back();
5377     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5378 }
5379 
PushItemFlag(ImGuiItemFlags option,bool enabled)5380 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5381 {
5382     ImGuiWindow* window = GetCurrentWindow();
5383     if (enabled)
5384         window->DC.ItemFlags |= option;
5385     else
5386         window->DC.ItemFlags &= ~option;
5387     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5388 }
5389 
PopItemFlag()5390 void ImGui::PopItemFlag()
5391 {
5392     ImGuiWindow* window = GetCurrentWindow();
5393     window->DC.ItemFlagsStack.pop_back();
5394     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5395 }
5396 
5397 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)5398 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5399 {
5400     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
5401 }
5402 
PopAllowKeyboardFocus()5403 void ImGui::PopAllowKeyboardFocus()
5404 {
5405     PopItemFlag();
5406 }
5407 
PushButtonRepeat(bool repeat)5408 void ImGui::PushButtonRepeat(bool repeat)
5409 {
5410     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5411 }
5412 
PopButtonRepeat()5413 void ImGui::PopButtonRepeat()
5414 {
5415     PopItemFlag();
5416 }
5417 
PushTextWrapPos(float wrap_pos_x)5418 void ImGui::PushTextWrapPos(float wrap_pos_x)
5419 {
5420     ImGuiWindow* window = GetCurrentWindow();
5421     window->DC.TextWrapPos = wrap_pos_x;
5422     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5423 }
5424 
PopTextWrapPos()5425 void ImGui::PopTextWrapPos()
5426 {
5427     ImGuiWindow* window = GetCurrentWindow();
5428     window->DC.TextWrapPosStack.pop_back();
5429     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5430 }
5431 
5432 // FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
PushStyleColor(ImGuiCol idx,ImU32 col)5433 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5434 {
5435     ImGuiContext& g = *GImGui;
5436     ImGuiColorMod backup;
5437     backup.Col = idx;
5438     backup.BackupValue = g.Style.Colors[idx];
5439     g.ColorModifiers.push_back(backup);
5440     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5441 }
5442 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)5443 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5444 {
5445     ImGuiContext& g = *GImGui;
5446     ImGuiColorMod backup;
5447     backup.Col = idx;
5448     backup.BackupValue = g.Style.Colors[idx];
5449     g.ColorModifiers.push_back(backup);
5450     g.Style.Colors[idx] = col;
5451 }
5452 
PopStyleColor(int count)5453 void ImGui::PopStyleColor(int count)
5454 {
5455     ImGuiContext& g = *GImGui;
5456     while (count > 0)
5457     {
5458         ImGuiColorMod& backup = g.ColorModifiers.back();
5459         g.Style.Colors[backup.Col] = backup.BackupValue;
5460         g.ColorModifiers.pop_back();
5461         count--;
5462     }
5463 }
5464 
5465 struct ImGuiStyleVarInfo
5466 {
5467     ImGuiDataType   Type;
5468     ImU32           Count;
5469     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo5470     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5471 };
5472 
5473 static const ImGuiStyleVarInfo GStyleVarInfo[] =
5474 {
5475     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },              // ImGuiStyleVar_Alpha
5476     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },      // ImGuiStyleVar_WindowPadding
5477     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },     // ImGuiStyleVar_WindowRounding
5478     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },   // ImGuiStyleVar_WindowBorderSize
5479     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },      // ImGuiStyleVar_WindowMinSize
5480     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },   // ImGuiStyleVar_WindowTitleAlign
5481     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },      // ImGuiStyleVar_ChildRounding
5482     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },    // ImGuiStyleVar_ChildBorderSize
5483     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },      // ImGuiStyleVar_PopupRounding
5484     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },    // ImGuiStyleVar_PopupBorderSize
5485     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },       // ImGuiStyleVar_FramePadding
5486     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },      // ImGuiStyleVar_FrameRounding
5487     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },    // ImGuiStyleVar_FrameBorderSize
5488     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },        // ImGuiStyleVar_ItemSpacing
5489     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },   // ImGuiStyleVar_ItemInnerSpacing
5490     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },      // ImGuiStyleVar_IndentSpacing
5491     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },      // ImGuiStyleVar_ScrollbarSize
5492     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },  // ImGuiStyleVar_ScrollbarRounding
5493     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },        // ImGuiStyleVar_GrabMinSize
5494     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },       // ImGuiStyleVar_GrabRounding
5495     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },    // ImGuiStyleVar_ButtonTextAlign
5496 };
5497 
GetStyleVarInfo(ImGuiStyleVar idx)5498 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5499 {
5500     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5501     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5502     return &GStyleVarInfo[idx];
5503 }
5504 
PushStyleVar(ImGuiStyleVar idx,float val)5505 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5506 {
5507     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5508     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5509     {
5510         ImGuiContext& g = *GImGui;
5511         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5512         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5513         *pvar = val;
5514         return;
5515     }
5516     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5517 }
5518 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)5519 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5520 {
5521     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5522     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5523     {
5524         ImGuiContext& g = *GImGui;
5525         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5526         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5527         *pvar = val;
5528         return;
5529     }
5530     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5531 }
5532 
PopStyleVar(int count)5533 void ImGui::PopStyleVar(int count)
5534 {
5535     ImGuiContext& g = *GImGui;
5536     while (count > 0)
5537     {
5538         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5539         ImGuiStyleMod& backup = g.StyleModifiers.back();
5540         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5541         void* data = info->GetVarPtr(&g.Style);
5542         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
5543         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5544         g.StyleModifiers.pop_back();
5545         count--;
5546     }
5547 }
5548 
GetStyleColorName(ImGuiCol idx)5549 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5550 {
5551     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5552     switch (idx)
5553     {
5554     case ImGuiCol_Text: return "Text";
5555     case ImGuiCol_TextDisabled: return "TextDisabled";
5556     case ImGuiCol_WindowBg: return "WindowBg";
5557     case ImGuiCol_ChildBg: return "ChildBg";
5558     case ImGuiCol_PopupBg: return "PopupBg";
5559     case ImGuiCol_Border: return "Border";
5560     case ImGuiCol_BorderShadow: return "BorderShadow";
5561     case ImGuiCol_FrameBg: return "FrameBg";
5562     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5563     case ImGuiCol_FrameBgActive: return "FrameBgActive";
5564     case ImGuiCol_TitleBg: return "TitleBg";
5565     case ImGuiCol_TitleBgActive: return "TitleBgActive";
5566     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5567     case ImGuiCol_MenuBarBg: return "MenuBarBg";
5568     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5569     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5570     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5571     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5572     case ImGuiCol_CheckMark: return "CheckMark";
5573     case ImGuiCol_SliderGrab: return "SliderGrab";
5574     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5575     case ImGuiCol_Button: return "Button";
5576     case ImGuiCol_ButtonHovered: return "ButtonHovered";
5577     case ImGuiCol_ButtonActive: return "ButtonActive";
5578     case ImGuiCol_Header: return "Header";
5579     case ImGuiCol_HeaderHovered: return "HeaderHovered";
5580     case ImGuiCol_HeaderActive: return "HeaderActive";
5581     case ImGuiCol_Separator: return "Separator";
5582     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5583     case ImGuiCol_SeparatorActive: return "SeparatorActive";
5584     case ImGuiCol_ResizeGrip: return "ResizeGrip";
5585     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5586     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5587     case ImGuiCol_PlotLines: return "PlotLines";
5588     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5589     case ImGuiCol_PlotHistogram: return "PlotHistogram";
5590     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5591     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5592     case ImGuiCol_DragDropTarget: return "DragDropTarget";
5593     case ImGuiCol_NavHighlight: return "NavHighlight";
5594     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5595     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5596     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5597     }
5598     IM_ASSERT(0);
5599     return "Unknown";
5600 }
5601 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)5602 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5603 {
5604     if (window->RootWindow == potential_parent)
5605         return true;
5606     while (window != NULL)
5607     {
5608         if (window == potential_parent)
5609             return true;
5610         window = window->ParentWindow;
5611     }
5612     return false;
5613 }
5614 
IsWindowHovered(ImGuiHoveredFlags flags)5615 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5616 {
5617     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
5618     ImGuiContext& g = *GImGui;
5619 
5620     if (flags & ImGuiHoveredFlags_AnyWindow)
5621     {
5622         if (g.HoveredWindow == NULL)
5623             return false;
5624     }
5625     else
5626     {
5627         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5628         {
5629         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5630             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5631                 return false;
5632             break;
5633         case ImGuiHoveredFlags_RootWindow:
5634             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5635                 return false;
5636             break;
5637         case ImGuiHoveredFlags_ChildWindows:
5638             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
5639                 return false;
5640             break;
5641         default:
5642             if (g.HoveredWindow != g.CurrentWindow)
5643                 return false;
5644             break;
5645         }
5646     }
5647 
5648     if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5649         return false;
5650     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5651         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5652             return false;
5653     return true;
5654 }
5655 
IsWindowFocused(ImGuiFocusedFlags flags)5656 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5657 {
5658     ImGuiContext& g = *GImGui;
5659 
5660     if (flags & ImGuiFocusedFlags_AnyWindow)
5661         return g.NavWindow != NULL;
5662 
5663     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
5664     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5665     {
5666     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5667         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5668     case ImGuiFocusedFlags_RootWindow:
5669         return g.NavWindow == g.CurrentWindow->RootWindow;
5670     case ImGuiFocusedFlags_ChildWindows:
5671         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
5672     default:
5673         return g.NavWindow == g.CurrentWindow;
5674     }
5675 }
5676 
5677 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
5678 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
5679 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)5680 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
5681 {
5682     return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
5683 }
5684 
GetWindowWidth()5685 float ImGui::GetWindowWidth()
5686 {
5687     ImGuiWindow* window = GImGui->CurrentWindow;
5688     return window->Size.x;
5689 }
5690 
GetWindowHeight()5691 float ImGui::GetWindowHeight()
5692 {
5693     ImGuiWindow* window = GImGui->CurrentWindow;
5694     return window->Size.y;
5695 }
5696 
GetWindowPos()5697 ImVec2 ImGui::GetWindowPos()
5698 {
5699     ImGuiContext& g = *GImGui;
5700     ImGuiWindow* window = g.CurrentWindow;
5701     return window->Pos;
5702 }
5703 
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)5704 void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
5705 {
5706     window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5707     window->Scroll.x = new_scroll_x;
5708     window->DC.CursorMaxPos.x -= window->Scroll.x;
5709 }
5710 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)5711 void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5712 {
5713     window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5714     window->Scroll.y = new_scroll_y;
5715     window->DC.CursorMaxPos.y -= window->Scroll.y;
5716 }
5717 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)5718 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5719 {
5720     // Test condition (NB: bit 0 is always true) and clear flags for next time
5721     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5722         return;
5723 
5724     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5725     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5726     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5727 
5728     // Set
5729     const ImVec2 old_pos = window->Pos;
5730     window->Pos = ImFloor(pos);
5731     window->DC.CursorPos += (window->Pos - old_pos);    // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
5732     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5733 }
5734 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)5735 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5736 {
5737     ImGuiWindow* window = GetCurrentWindowRead();
5738     SetWindowPos(window, pos, cond);
5739 }
5740 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)5741 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5742 {
5743     if (ImGuiWindow* window = FindWindowByName(name))
5744         SetWindowPos(window, pos, cond);
5745 }
5746 
GetWindowSize()5747 ImVec2 ImGui::GetWindowSize()
5748 {
5749     ImGuiWindow* window = GetCurrentWindowRead();
5750     return window->Size;
5751 }
5752 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)5753 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5754 {
5755     // Test condition (NB: bit 0 is always true) and clear flags for next time
5756     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5757         return;
5758 
5759     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5760     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5761 
5762     // Set
5763     if (size.x > 0.0f)
5764     {
5765         window->AutoFitFramesX = 0;
5766         window->SizeFull.x = ImFloor(size.x);
5767     }
5768     else
5769     {
5770         window->AutoFitFramesX = 2;
5771         window->AutoFitOnlyGrows = false;
5772     }
5773     if (size.y > 0.0f)
5774     {
5775         window->AutoFitFramesY = 0;
5776         window->SizeFull.y = ImFloor(size.y);
5777     }
5778     else
5779     {
5780         window->AutoFitFramesY = 2;
5781         window->AutoFitOnlyGrows = false;
5782     }
5783 }
5784 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)5785 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5786 {
5787     SetWindowSize(GImGui->CurrentWindow, size, cond);
5788 }
5789 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)5790 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5791 {
5792     if (ImGuiWindow* window = FindWindowByName(name))
5793         SetWindowSize(window, size, cond);
5794 }
5795 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)5796 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
5797 {
5798     // Test condition (NB: bit 0 is always true) and clear flags for next time
5799     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
5800         return;
5801     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5802 
5803     // Set
5804     window->Collapsed = collapsed;
5805 }
5806 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)5807 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
5808 {
5809     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
5810 }
5811 
IsWindowCollapsed()5812 bool ImGui::IsWindowCollapsed()
5813 {
5814     ImGuiWindow* window = GetCurrentWindowRead();
5815     return window->Collapsed;
5816 }
5817 
IsWindowAppearing()5818 bool ImGui::IsWindowAppearing()
5819 {
5820     ImGuiWindow* window = GetCurrentWindowRead();
5821     return window->Appearing;
5822 }
5823 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)5824 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
5825 {
5826     if (ImGuiWindow* window = FindWindowByName(name))
5827         SetWindowCollapsed(window, collapsed, cond);
5828 }
5829 
SetWindowFocus()5830 void ImGui::SetWindowFocus()
5831 {
5832     FocusWindow(GImGui->CurrentWindow);
5833 }
5834 
SetWindowFocus(const char * name)5835 void ImGui::SetWindowFocus(const char* name)
5836 {
5837     if (name)
5838     {
5839         if (ImGuiWindow* window = FindWindowByName(name))
5840             FocusWindow(window);
5841     }
5842     else
5843     {
5844         FocusWindow(NULL);
5845     }
5846 }
5847 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)5848 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
5849 {
5850     ImGuiContext& g = *GImGui;
5851     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5852     g.NextWindowData.PosVal = pos;
5853     g.NextWindowData.PosPivotVal = pivot;
5854     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
5855 }
5856 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)5857 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
5858 {
5859     ImGuiContext& g = *GImGui;
5860     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5861     g.NextWindowData.SizeVal = size;
5862     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
5863 }
5864 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)5865 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
5866 {
5867     ImGuiContext& g = *GImGui;
5868     g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
5869     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
5870     g.NextWindowData.SizeCallback = custom_callback;
5871     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
5872 }
5873 
SetNextWindowContentSize(const ImVec2 & size)5874 void ImGui::SetNextWindowContentSize(const ImVec2& size)
5875 {
5876     ImGuiContext& g = *GImGui;
5877     g.NextWindowData.ContentSizeVal = size;  // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value.
5878     g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
5879 }
5880 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)5881 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
5882 {
5883     ImGuiContext& g = *GImGui;
5884     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5885     g.NextWindowData.CollapsedVal = collapsed;
5886     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
5887 }
5888 
SetNextWindowFocus()5889 void ImGui::SetNextWindowFocus()
5890 {
5891     ImGuiContext& g = *GImGui;
5892     g.NextWindowData.FocusCond = ImGuiCond_Always;   // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5893 }
5894 
SetNextWindowBgAlpha(float alpha)5895 void ImGui::SetNextWindowBgAlpha(float alpha)
5896 {
5897     ImGuiContext& g = *GImGui;
5898     g.NextWindowData.BgAlphaVal = alpha;
5899     g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5900 }
5901 
5902 // In window space (not screen space!)
GetContentRegionMax()5903 ImVec2 ImGui::GetContentRegionMax()
5904 {
5905     ImGuiWindow* window = GetCurrentWindowRead();
5906     ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
5907     if (window->DC.ColumnsSet)
5908         mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
5909     return mx;
5910 }
5911 
GetContentRegionAvail()5912 ImVec2 ImGui::GetContentRegionAvail()
5913 {
5914     ImGuiWindow* window = GetCurrentWindowRead();
5915     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5916 }
5917 
GetContentRegionAvailWidth()5918 float ImGui::GetContentRegionAvailWidth()
5919 {
5920     return GetContentRegionAvail().x;
5921 }
5922 
5923 // In window space (not screen space!)
GetWindowContentRegionMin()5924 ImVec2 ImGui::GetWindowContentRegionMin()
5925 {
5926     ImGuiWindow* window = GetCurrentWindowRead();
5927     return window->ContentsRegionRect.Min - window->Pos;
5928 }
5929 
GetWindowContentRegionMax()5930 ImVec2 ImGui::GetWindowContentRegionMax()
5931 {
5932     ImGuiWindow* window = GetCurrentWindowRead();
5933     return window->ContentsRegionRect.Max - window->Pos;
5934 }
5935 
GetWindowContentRegionWidth()5936 float ImGui::GetWindowContentRegionWidth()
5937 {
5938     ImGuiWindow* window = GetCurrentWindowRead();
5939     return window->ContentsRegionRect.GetWidth();
5940 }
5941 
GetTextLineHeight()5942 float ImGui::GetTextLineHeight()
5943 {
5944     ImGuiContext& g = *GImGui;
5945     return g.FontSize;
5946 }
5947 
GetTextLineHeightWithSpacing()5948 float ImGui::GetTextLineHeightWithSpacing()
5949 {
5950     ImGuiContext& g = *GImGui;
5951     return g.FontSize + g.Style.ItemSpacing.y;
5952 }
5953 
GetFrameHeight()5954 float ImGui::GetFrameHeight()
5955 {
5956     ImGuiContext& g = *GImGui;
5957     return g.FontSize + g.Style.FramePadding.y * 2.0f;
5958 }
5959 
GetFrameHeightWithSpacing()5960 float ImGui::GetFrameHeightWithSpacing()
5961 {
5962     ImGuiContext& g = *GImGui;
5963     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5964 }
5965 
GetWindowDrawList()5966 ImDrawList* ImGui::GetWindowDrawList()
5967 {
5968     ImGuiWindow* window = GetCurrentWindow();
5969     return window->DrawList;
5970 }
5971 
GetFont()5972 ImFont* ImGui::GetFont()
5973 {
5974     return GImGui->Font;
5975 }
5976 
GetFontSize()5977 float ImGui::GetFontSize()
5978 {
5979     return GImGui->FontSize;
5980 }
5981 
GetFontTexUvWhitePixel()5982 ImVec2 ImGui::GetFontTexUvWhitePixel()
5983 {
5984     return GImGui->DrawListSharedData.TexUvWhitePixel;
5985 }
5986 
SetWindowFontScale(float scale)5987 void ImGui::SetWindowFontScale(float scale)
5988 {
5989     ImGuiContext& g = *GImGui;
5990     ImGuiWindow* window = GetCurrentWindow();
5991     window->FontWindowScale = scale;
5992     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
5993 }
5994 
5995 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5996 // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
GetCursorPos()5997 ImVec2 ImGui::GetCursorPos()
5998 {
5999     ImGuiWindow* window = GetCurrentWindowRead();
6000     return window->DC.CursorPos - window->Pos + window->Scroll;
6001 }
6002 
GetCursorPosX()6003 float ImGui::GetCursorPosX()
6004 {
6005     ImGuiWindow* window = GetCurrentWindowRead();
6006     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6007 }
6008 
GetCursorPosY()6009 float ImGui::GetCursorPosY()
6010 {
6011     ImGuiWindow* window = GetCurrentWindowRead();
6012     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6013 }
6014 
SetCursorPos(const ImVec2 & local_pos)6015 void ImGui::SetCursorPos(const ImVec2& local_pos)
6016 {
6017     ImGuiWindow* window = GetCurrentWindow();
6018     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6019     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6020 }
6021 
SetCursorPosX(float x)6022 void ImGui::SetCursorPosX(float x)
6023 {
6024     ImGuiWindow* window = GetCurrentWindow();
6025     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6026     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6027 }
6028 
SetCursorPosY(float y)6029 void ImGui::SetCursorPosY(float y)
6030 {
6031     ImGuiWindow* window = GetCurrentWindow();
6032     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6033     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6034 }
6035 
GetCursorStartPos()6036 ImVec2 ImGui::GetCursorStartPos()
6037 {
6038     ImGuiWindow* window = GetCurrentWindowRead();
6039     return window->DC.CursorStartPos - window->Pos;
6040 }
6041 
GetCursorScreenPos()6042 ImVec2 ImGui::GetCursorScreenPos()
6043 {
6044     ImGuiWindow* window = GetCurrentWindowRead();
6045     return window->DC.CursorPos;
6046 }
6047 
SetCursorScreenPos(const ImVec2 & screen_pos)6048 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
6049 {
6050     ImGuiWindow* window = GetCurrentWindow();
6051     window->DC.CursorPos = screen_pos;
6052     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6053 }
6054 
GetScrollX()6055 float ImGui::GetScrollX()
6056 {
6057     return GImGui->CurrentWindow->Scroll.x;
6058 }
6059 
GetScrollY()6060 float ImGui::GetScrollY()
6061 {
6062     return GImGui->CurrentWindow->Scroll.y;
6063 }
6064 
GetScrollMaxX()6065 float ImGui::GetScrollMaxX()
6066 {
6067     return GetWindowScrollMaxX(GImGui->CurrentWindow);
6068 }
6069 
GetScrollMaxY()6070 float ImGui::GetScrollMaxY()
6071 {
6072     return GetWindowScrollMaxY(GImGui->CurrentWindow);
6073 }
6074 
SetScrollX(float scroll_x)6075 void ImGui::SetScrollX(float scroll_x)
6076 {
6077     ImGuiWindow* window = GetCurrentWindow();
6078     window->ScrollTarget.x = scroll_x;
6079     window->ScrollTargetCenterRatio.x = 0.0f;
6080 }
6081 
SetScrollY(float scroll_y)6082 void ImGui::SetScrollY(float scroll_y)
6083 {
6084     ImGuiWindow* window = GetCurrentWindow();
6085     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
6086     window->ScrollTargetCenterRatio.y = 0.0f;
6087 }
6088 
SetScrollFromPosY(float pos_y,float center_y_ratio)6089 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
6090 {
6091     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
6092     ImGuiWindow* window = GetCurrentWindow();
6093     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
6094     window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
6095     window->ScrollTargetCenterRatio.y = center_y_ratio;
6096 }
6097 
6098 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHereY(float center_y_ratio)6099 void ImGui::SetScrollHereY(float center_y_ratio)
6100 {
6101     ImGuiWindow* window = GetCurrentWindow();
6102     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
6103     target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
6104     SetScrollFromPosY(target_y, center_y_ratio);
6105 }
6106 
ActivateItem(ImGuiID id)6107 void ImGui::ActivateItem(ImGuiID id)
6108 {
6109     ImGuiContext& g = *GImGui;
6110     g.NavNextActivateId = id;
6111 }
6112 
SetKeyboardFocusHere(int offset)6113 void ImGui::SetKeyboardFocusHere(int offset)
6114 {
6115     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6116     ImGuiWindow* window = GetCurrentWindow();
6117     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6118     window->FocusIdxTabRequestNext = INT_MAX;
6119 }
6120 
SetItemDefaultFocus()6121 void ImGui::SetItemDefaultFocus()
6122 {
6123     ImGuiContext& g = *GImGui;
6124     ImGuiWindow* window = g.CurrentWindow;
6125     if (!window->Appearing)
6126         return;
6127     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6128     {
6129         g.NavInitRequest = false;
6130         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6131         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6132         NavUpdateAnyRequestFlag();
6133         if (!IsItemVisible())
6134             SetScrollHereY();
6135     }
6136 }
6137 
SetStateStorage(ImGuiStorage * tree)6138 void ImGui::SetStateStorage(ImGuiStorage* tree)
6139 {
6140     ImGuiWindow* window = GetCurrentWindow();
6141     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6142 }
6143 
GetStateStorage()6144 ImGuiStorage* ImGui::GetStateStorage()
6145 {
6146     ImGuiWindow* window = GetCurrentWindowRead();
6147     return window->DC.StateStorage;
6148 }
6149 
PushID(const char * str_id)6150 void ImGui::PushID(const char* str_id)
6151 {
6152     ImGuiWindow* window = GetCurrentWindowRead();
6153     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6154 }
6155 
PushID(const char * str_id_begin,const char * str_id_end)6156 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6157 {
6158     ImGuiWindow* window = GetCurrentWindowRead();
6159     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6160 }
6161 
PushID(const void * ptr_id)6162 void ImGui::PushID(const void* ptr_id)
6163 {
6164     ImGuiWindow* window = GetCurrentWindowRead();
6165     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6166 }
6167 
PushID(int int_id)6168 void ImGui::PushID(int int_id)
6169 {
6170     const void* ptr_id = (void*)(intptr_t)int_id;
6171     ImGuiWindow* window = GetCurrentWindowRead();
6172     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6173 }
6174 
PopID()6175 void ImGui::PopID()
6176 {
6177     ImGuiWindow* window = GetCurrentWindowRead();
6178     window->IDStack.pop_back();
6179 }
6180 
GetID(const char * str_id)6181 ImGuiID ImGui::GetID(const char* str_id)
6182 {
6183     return GImGui->CurrentWindow->GetID(str_id);
6184 }
6185 
GetID(const char * str_id_begin,const char * str_id_end)6186 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6187 {
6188     return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6189 }
6190 
GetID(const void * ptr_id)6191 ImGuiID ImGui::GetID(const void* ptr_id)
6192 {
6193     return GImGui->CurrentWindow->GetID(ptr_id);
6194 }
6195 
IsRectVisible(const ImVec2 & size)6196 bool ImGui::IsRectVisible(const ImVec2& size)
6197 {
6198     ImGuiWindow* window = GetCurrentWindowRead();
6199     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6200 }
6201 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6202 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6203 {
6204     ImGuiWindow* window = GetCurrentWindowRead();
6205     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6206 }
6207 
6208 // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
BeginGroup()6209 void ImGui::BeginGroup()
6210 {
6211     ImGuiContext& g = *GImGui;
6212     ImGuiWindow* window = GetCurrentWindow();
6213 
6214     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6215     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6216     group_data.BackupCursorPos = window->DC.CursorPos;
6217     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6218     group_data.BackupIndent = window->DC.Indent;
6219     group_data.BackupGroupOffset = window->DC.GroupOffset;
6220     group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6221     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6222     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6223     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6224     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6225     group_data.AdvanceCursor = true;
6226 
6227     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6228     window->DC.Indent = window->DC.GroupOffset;
6229     window->DC.CursorMaxPos = window->DC.CursorPos;
6230     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6231     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6232 }
6233 
EndGroup()6234 void ImGui::EndGroup()
6235 {
6236     ImGuiContext& g = *GImGui;
6237     ImGuiWindow* window = GetCurrentWindow();
6238     IM_ASSERT(!window->DC.GroupStack.empty());    // Mismatched BeginGroup()/EndGroup() calls
6239 
6240     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6241 
6242     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6243     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
6244 
6245     window->DC.CursorPos = group_data.BackupCursorPos;
6246     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6247     window->DC.Indent = group_data.BackupIndent;
6248     window->DC.GroupOffset = group_data.BackupGroupOffset;
6249     window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6250     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6251     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6252 
6253     if (group_data.AdvanceCursor)
6254     {
6255         window->DC.CurrentLineTextBaseOffset = ImMax(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.
6256         ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
6257         ItemAdd(group_bb, 0);
6258     }
6259 
6260     // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
6261     // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
6262     // (and if you grep for LastItemId you'll notice it is only used in that context.
6263     if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6264         window->DC.LastItemId = g.ActiveId;
6265     else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6266         window->DC.LastItemId = g.ActiveIdPreviousFrame;
6267     window->DC.LastItemRect = group_bb;
6268 
6269     window->DC.GroupStack.pop_back();
6270 
6271     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
6272 }
6273 
6274 // Gets back to previous line and continue with horizontal layout
6275 //      pos_x == 0      : follow right after previous item
6276 //      pos_x != 0      : align to specified x position (relative to window/group left)
6277 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
6278 //      spacing_w >= 0  : enforce spacing amount
SameLine(float pos_x,float spacing_w)6279 void ImGui::SameLine(float pos_x, float spacing_w)
6280 {
6281     ImGuiWindow* window = GetCurrentWindow();
6282     if (window->SkipItems)
6283         return;
6284 
6285     ImGuiContext& g = *GImGui;
6286     if (pos_x != 0.0f)
6287     {
6288         if (spacing_w < 0.0f) spacing_w = 0.0f;
6289         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6290         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6291     }
6292     else
6293     {
6294         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6295         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6296         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6297     }
6298     window->DC.CurrentLineSize = window->DC.PrevLineSize;
6299     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6300 }
6301 
Indent(float indent_w)6302 void ImGui::Indent(float indent_w)
6303 {
6304     ImGuiContext& g = *GImGui;
6305     ImGuiWindow* window = GetCurrentWindow();
6306     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6307     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6308 }
6309 
Unindent(float indent_w)6310 void ImGui::Unindent(float indent_w)
6311 {
6312     ImGuiContext& g = *GImGui;
6313     ImGuiWindow* window = GetCurrentWindow();
6314     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6315     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6316 }
6317 
6318 //-----------------------------------------------------------------------------
6319 // [SECTION] TOOLTIPS
6320 //-----------------------------------------------------------------------------
6321 
BeginTooltip()6322 void ImGui::BeginTooltip()
6323 {
6324     ImGuiContext& g = *GImGui;
6325     if (g.DragDropWithinSourceOrTarget)
6326     {
6327         // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor)
6328         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6329         // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do.
6330         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6331         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6332         SetNextWindowPos(tooltip_pos);
6333         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6334         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6335         BeginTooltipEx(0, true);
6336     }
6337     else
6338     {
6339         BeginTooltipEx(0, false);
6340     }
6341 }
6342 
6343 // 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)6344 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6345 {
6346     ImGuiContext& g = *GImGui;
6347     char window_name[16];
6348     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
6349     if (override_previous_tooltip)
6350         if (ImGuiWindow* window = FindWindowByName(window_name))
6351             if (window->Active)
6352             {
6353                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6354                 window->Hidden = true;
6355                 window->HiddenFramesRegular = 1;
6356                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
6357             }
6358     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
6359     Begin(window_name, NULL, flags | extra_flags);
6360 }
6361 
EndTooltip()6362 void ImGui::EndTooltip()
6363 {
6364     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
6365     End();
6366 }
6367 
SetTooltipV(const char * fmt,va_list args)6368 void ImGui::SetTooltipV(const char* fmt, va_list args)
6369 {
6370     ImGuiContext& g = *GImGui;
6371     if (g.DragDropWithinSourceOrTarget)
6372         BeginTooltip();
6373     else
6374         BeginTooltipEx(0, true);
6375     TextV(fmt, args);
6376     EndTooltip();
6377 }
6378 
SetTooltip(const char * fmt,...)6379 void ImGui::SetTooltip(const char* fmt, ...)
6380 {
6381     va_list args;
6382     va_start(args, fmt);
6383     SetTooltipV(fmt, args);
6384     va_end(args);
6385 }
6386 
6387 //-----------------------------------------------------------------------------
6388 // [SECTION] POPUPS
6389 //-----------------------------------------------------------------------------
6390 
IsPopupOpen(ImGuiID id)6391 bool ImGui::IsPopupOpen(ImGuiID id)
6392 {
6393     ImGuiContext& g = *GImGui;
6394     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
6395 }
6396 
IsPopupOpen(const char * str_id)6397 bool ImGui::IsPopupOpen(const char* str_id)
6398 {
6399     ImGuiContext& g = *GImGui;
6400     return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
6401 }
6402 
GetFrontMostPopupModal()6403 ImGuiWindow* ImGui::GetFrontMostPopupModal()
6404 {
6405     ImGuiContext& g = *GImGui;
6406     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6407         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6408             if (popup->Flags & ImGuiWindowFlags_Modal)
6409                 return popup;
6410     return NULL;
6411 }
6412 
OpenPopup(const char * str_id)6413 void ImGui::OpenPopup(const char* str_id)
6414 {
6415     ImGuiContext& g = *GImGui;
6416     OpenPopupEx(g.CurrentWindow->GetID(str_id));
6417 }
6418 
6419 // Mark popup as open (toggle toward open state).
6420 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6421 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6422 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)6423 void ImGui::OpenPopupEx(ImGuiID id)
6424 {
6425     ImGuiContext& g = *GImGui;
6426     ImGuiWindow* parent_window = g.CurrentWindow;
6427     int current_stack_size = g.CurrentPopupStack.Size;
6428     ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6429     popup_ref.PopupId = id;
6430     popup_ref.Window = NULL;
6431     popup_ref.ParentWindow = parent_window;
6432     popup_ref.OpenFrameCount = g.FrameCount;
6433     popup_ref.OpenParentId = parent_window->IDStack.back();
6434     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6435     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
6436 
6437     //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6438     if (g.OpenPopupStack.Size < current_stack_size + 1)
6439     {
6440         g.OpenPopupStack.push_back(popup_ref);
6441     }
6442     else
6443     {
6444         // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui
6445         // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing
6446         // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand.
6447         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6448         {
6449             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6450         }
6451         else
6452         {
6453             // Close child popups if any, then flag popup for open/reopen
6454             g.OpenPopupStack.resize(current_stack_size + 1);
6455             g.OpenPopupStack[current_stack_size] = popup_ref;
6456         }
6457 
6458         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6459         // This is equivalent to what ClosePopupToLevel() does.
6460         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
6461         //    FocusWindow(parent_window);
6462     }
6463 }
6464 
OpenPopupOnItemClick(const char * str_id,int mouse_button)6465 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6466 {
6467     ImGuiWindow* window = GImGui->CurrentWindow;
6468     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6469     {
6470         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!
6471         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6472         OpenPopupEx(id);
6473         return true;
6474     }
6475     return false;
6476 }
6477 
ClosePopupsOverWindow(ImGuiWindow * ref_window)6478 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6479 {
6480     ImGuiContext& g = *GImGui;
6481     if (g.OpenPopupStack.empty())
6482         return;
6483 
6484     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6485     // Don't close our own child popup windows.
6486     int n = 0;
6487     if (ref_window)
6488     {
6489         for (n = 0; n < g.OpenPopupStack.Size; n++)
6490         {
6491             ImGuiPopupRef& popup = g.OpenPopupStack[n];
6492             if (!popup.Window)
6493                 continue;
6494             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6495             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6496                 continue;
6497 
6498             // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6499             bool has_focus = false;
6500             for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
6501                 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
6502             if (!has_focus)
6503                 break;
6504         }
6505     }
6506     if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
6507         ClosePopupToLevel(n);
6508 }
6509 
ClosePopupToLevel(int remaining)6510 void ImGui::ClosePopupToLevel(int remaining)
6511 {
6512     IM_ASSERT(remaining >= 0);
6513     ImGuiContext& g = *GImGui;
6514     ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6515     if (g.NavLayer == 0)
6516         focus_window = NavRestoreLastChildNavWindow(focus_window);
6517     FocusWindow(focus_window);
6518     focus_window->DC.NavHideHighlightOneFrame = true;
6519     g.OpenPopupStack.resize(remaining);
6520 }
6521 
ClosePopup(ImGuiID id)6522 void ImGui::ClosePopup(ImGuiID id)
6523 {
6524     if (!IsPopupOpen(id))
6525         return;
6526     ImGuiContext& g = *GImGui;
6527     ClosePopupToLevel(g.OpenPopupStack.Size - 1);
6528 }
6529 
6530 // Close the popup we have begin-ed into.
CloseCurrentPopup()6531 void ImGui::CloseCurrentPopup()
6532 {
6533     ImGuiContext& g = *GImGui;
6534     int popup_idx = g.CurrentPopupStack.Size - 1;
6535     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6536         return;
6537     while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
6538         popup_idx--;
6539     ClosePopupToLevel(popup_idx);
6540 }
6541 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)6542 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6543 {
6544     ImGuiContext& g = *GImGui;
6545     if (!IsPopupOpen(id))
6546     {
6547         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6548         return false;
6549     }
6550 
6551     char name[20];
6552     if (extra_flags & ImGuiWindowFlags_ChildMenu)
6553         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size);    // Recycle windows based on depth
6554     else
6555         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6556 
6557     bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
6558     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6559         EndPopup();
6560 
6561     return is_open;
6562 }
6563 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)6564 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6565 {
6566     ImGuiContext& g = *GImGui;
6567     if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
6568     {
6569         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6570         return false;
6571     }
6572     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6573 }
6574 
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)6575 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6576 {
6577     ImGuiContext& g = *GImGui;
6578     ImGuiWindow* window = g.CurrentWindow;
6579     const ImGuiID id = window->GetID(name);
6580     if (!IsPopupOpen(id))
6581     {
6582         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6583         return false;
6584     }
6585 
6586     // Center modal windows by default
6587     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6588     if (g.NextWindowData.PosCond == 0)
6589         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
6590 
6591     bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
6592     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6593     {
6594         EndPopup();
6595         if (is_open)
6596             ClosePopup(id);
6597         return false;
6598     }
6599     return is_open;
6600 }
6601 
EndPopup()6602 void ImGui::EndPopup()
6603 {
6604     ImGuiContext& g = *GImGui; (void)g;
6605     IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
6606     IM_ASSERT(g.CurrentPopupStack.Size > 0);
6607 
6608     // Make all menus and popups wrap around for now, may need to expose that policy.
6609     NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
6610 
6611     End();
6612 }
6613 
6614 // This is a helper to handle the simplest case of associating one named popup to one given widget.
6615 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6616 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)6617 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6618 {
6619     ImGuiWindow* window = GImGui->CurrentWindow;
6620     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!
6621     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6622     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6623         OpenPopupEx(id);
6624     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6625 }
6626 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)6627 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6628 {
6629     if (!str_id)
6630         str_id = "window_context";
6631     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6632     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6633         if (also_over_items || !IsAnyItemHovered())
6634             OpenPopupEx(id);
6635     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6636 }
6637 
BeginPopupContextVoid(const char * str_id,int mouse_button)6638 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
6639 {
6640     if (!str_id)
6641         str_id = "void_context";
6642     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6643     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
6644         OpenPopupEx(id);
6645     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6646 }
6647 
GetWindowAllowedExtentRect(ImGuiWindow *)6648 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
6649 {
6650     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
6651     ImRect r_screen = GetViewportRect();
6652     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
6653     return r_screen;
6654 }
6655 
6656 // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
6657 // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy)6658 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
6659 {
6660     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
6661     //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
6662     //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
6663 
6664     // Combo Box policy (we want a connecting edge)
6665     if (policy == ImGuiPopupPositionPolicy_ComboBox)
6666     {
6667         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
6668         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6669         {
6670             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6671             if (n != -1 && dir == *last_dir) // Already tried this direction?
6672                 continue;
6673             ImVec2 pos;
6674             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
6675             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
6676             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
6677             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
6678             if (!r_outer.Contains(ImRect(pos, pos + size)))
6679                 continue;
6680             *last_dir = dir;
6681             return pos;
6682         }
6683     }
6684 
6685     // Default popup policy
6686     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
6687     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6688     {
6689         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6690         if (n != -1 && dir == *last_dir) // Already tried this direction?
6691             continue;
6692         float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
6693         float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
6694         if (avail_w < size.x || avail_h < size.y)
6695             continue;
6696         ImVec2 pos;
6697         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
6698         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
6699         *last_dir = dir;
6700         return pos;
6701     }
6702 
6703     // Fallback, try to keep within display
6704     *last_dir = ImGuiDir_None;
6705     ImVec2 pos = ref_pos;
6706     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
6707     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
6708     return pos;
6709 }
6710 
FindBestWindowPosForPopup(ImGuiWindow * window)6711 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
6712 {
6713     ImGuiContext& g = *GImGui;
6714 
6715     ImRect r_outer = GetWindowAllowedExtentRect(window);
6716     if (window->Flags & ImGuiWindowFlags_ChildMenu)
6717     {
6718         // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds.
6719         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
6720         IM_ASSERT(g.CurrentWindow == window);
6721         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
6722         float horizontal_overlap = g.Style.ItemSpacing.x;       // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
6723         ImRect r_avoid;
6724         if (parent_window->DC.MenuBarAppending)
6725             r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
6726         else
6727             r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
6728         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6729     }
6730     if (window->Flags & ImGuiWindowFlags_Popup)
6731     {
6732         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
6733         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6734     }
6735     if (window->Flags & ImGuiWindowFlags_Tooltip)
6736     {
6737         // Position tooltip (always follows mouse)
6738         float sc = g.Style.MouseCursorScale;
6739         ImVec2 ref_pos = NavCalcPreferredRefPos();
6740         ImRect r_avoid;
6741         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
6742             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
6743         else
6744             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
6745         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6746         if (window->AutoPosLastDirection == ImGuiDir_None)
6747             pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
6748         return pos;
6749     }
6750     IM_ASSERT(0);
6751     return window->Pos;
6752 }
6753 
6754 //-----------------------------------------------------------------------------
6755 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
6756 //-----------------------------------------------------------------------------
6757 
ImGetDirQuadrantFromDelta(float dx,float dy)6758 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
6759 {
6760     if (ImFabs(dx) > ImFabs(dy))
6761         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
6762     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
6763 }
6764 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)6765 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
6766 {
6767     if (a1 < b0)
6768         return a1 - b0;
6769     if (b1 < a0)
6770         return a0 - b1;
6771     return 0.0f;
6772 }
6773 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)6774 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
6775 {
6776     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
6777     {
6778         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
6779         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
6780     }
6781     else
6782     {
6783         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
6784         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
6785     }
6786 }
6787 
6788 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)6789 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
6790 {
6791     ImGuiContext& g = *GImGui;
6792     ImGuiWindow* window = g.CurrentWindow;
6793     if (g.NavLayer != window->DC.NavLayerCurrent)
6794         return false;
6795 
6796     const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
6797     g.NavScoringCount++;
6798 
6799     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
6800     if (window->ParentWindow == g.NavWindow)
6801     {
6802         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
6803         if (!window->ClipRect.Contains(cand))
6804             return false;
6805         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
6806     }
6807 
6808     // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
6809     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
6810     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
6811 
6812     // Compute distance between boxes
6813     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
6814     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
6815     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
6816     if (dby != 0.0f && dbx != 0.0f)
6817        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
6818     float dist_box = ImFabs(dbx) + ImFabs(dby);
6819 
6820     // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
6821     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
6822     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
6823     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
6824 
6825     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
6826     ImGuiDir quadrant;
6827     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
6828     if (dbx != 0.0f || dby != 0.0f)
6829     {
6830         // For non-overlapping boxes, use distance between boxes
6831         dax = dbx;
6832         day = dby;
6833         dist_axial = dist_box;
6834         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
6835     }
6836     else if (dcx != 0.0f || dcy != 0.0f)
6837     {
6838         // For overlapping boxes with different centers, use distance between centers
6839         dax = dcx;
6840         day = dcy;
6841         dist_axial = dist_center;
6842         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
6843     }
6844     else
6845     {
6846         // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)
6847         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
6848     }
6849 
6850 #if IMGUI_DEBUG_NAV_SCORING
6851     char buf[128];
6852     if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
6853     {
6854         ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]);
6855         ImDrawList* draw_list = GetOverlayDrawList(window);
6856         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
6857         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
6858         draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
6859         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
6860     }
6861     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
6862     {
6863         if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
6864         if (quadrant == g.NavMoveDir)
6865         {
6866             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
6867             ImDrawList* draw_list = GetOverlayDrawList(window);
6868             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
6869             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
6870         }
6871     }
6872  #endif
6873 
6874     // Is it in the quadrant we're interesting in moving to?
6875     bool new_best = false;
6876     if (quadrant == g.NavMoveDir)
6877     {
6878         // Does it beat the current best candidate?
6879         if (dist_box < result->DistBox)
6880         {
6881             result->DistBox = dist_box;
6882             result->DistCenter = dist_center;
6883             return true;
6884         }
6885         if (dist_box == result->DistBox)
6886         {
6887             // Try using distance between center points to break ties
6888             if (dist_center < result->DistCenter)
6889             {
6890                 result->DistCenter = dist_center;
6891                 new_best = true;
6892             }
6893             else if (dist_center == result->DistCenter)
6894             {
6895                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
6896                 // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
6897                 // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
6898                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
6899                     new_best = true;
6900             }
6901         }
6902     }
6903 
6904     // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches
6905     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
6906     // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
6907     // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
6908     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
6909     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
6910         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
6911             if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
6912             {
6913                 result->DistAxial = dist_axial;
6914                 new_best = true;
6915             }
6916 
6917     return new_best;
6918 }
6919 
6920 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
NavProcessItem(ImGuiWindow * window,const ImRect & nav_bb,const ImGuiID id)6921 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
6922 {
6923     ImGuiContext& g = *GImGui;
6924     //if (!g.IO.NavActive)  // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
6925     //    return;
6926 
6927     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
6928     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
6929 
6930     // Process Init Request
6931     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
6932     {
6933         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
6934         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
6935         {
6936             g.NavInitResultId = id;
6937             g.NavInitResultRectRel = nav_bb_rel;
6938         }
6939         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
6940         {
6941             g.NavInitRequest = false; // Found a match, clear request
6942             NavUpdateAnyRequestFlag();
6943         }
6944     }
6945 
6946     // Process Move Request (scoring for navigation)
6947     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
6948     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
6949     {
6950         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
6951 #if IMGUI_DEBUG_NAV_SCORING
6952         // [DEBUG] Score all items in NavWindow at all times
6953         if (!g.NavMoveRequest)
6954             g.NavMoveDir = g.NavMoveDirLast;
6955         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
6956 #else
6957         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
6958 #endif
6959         if (new_best)
6960         {
6961             result->ID = id;
6962             result->Window = window;
6963             result->RectRel = nav_bb_rel;
6964         }
6965 
6966         const float VISIBLE_RATIO = 0.70f;
6967         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
6968             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)
6969                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
6970                 {
6971                     result = &g.NavMoveResultLocalVisibleSet;
6972                     result->ID = id;
6973                     result->Window = window;
6974                     result->RectRel = nav_bb_rel;
6975                 }
6976     }
6977 
6978     // Update window-relative bounding box of navigated item
6979     if (g.NavId == id)
6980     {
6981         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
6982         g.NavLayer = window->DC.NavLayerCurrent;
6983         g.NavIdIsAlive = true;
6984         g.NavIdTabCounter = window->FocusIdxTabCounter;
6985         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
6986     }
6987 }
6988 
NavMoveRequestButNoResultYet()6989 bool ImGui::NavMoveRequestButNoResultYet()
6990 {
6991     ImGuiContext& g = *GImGui;
6992     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
6993 }
6994 
NavMoveRequestCancel()6995 void ImGui::NavMoveRequestCancel()
6996 {
6997     ImGuiContext& g = *GImGui;
6998     g.NavMoveRequest = false;
6999     NavUpdateAnyRequestFlag();
7000 }
7001 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7002 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7003 {
7004     ImGuiContext& g = *GImGui;
7005     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7006     ImGui::NavMoveRequestCancel();
7007     g.NavMoveDir = move_dir;
7008     g.NavMoveClipDir = clip_dir;
7009     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7010     g.NavMoveRequestFlags = move_flags;
7011     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7012 }
7013 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7014 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7015 {
7016     ImGuiContext& g = *GImGui;
7017     if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7018         return;
7019     IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7020     ImRect bb_rel = window->NavRectRel[0];
7021 
7022     ImGuiDir clip_dir = g.NavMoveDir;
7023     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7024     {
7025         bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
7026         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7027         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7028     }
7029     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7030     {
7031         bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7032         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7033         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7034     }
7035     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7036     {
7037         bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
7038         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7039         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7040     }
7041     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7042     {
7043         bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7044         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7045         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7046     }
7047 }
7048 
NavSaveLastChildNavWindow(ImGuiWindow * nav_window)7049 static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
7050 {
7051     ImGuiWindow* parent_window = nav_window;
7052     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7053         parent_window = parent_window->ParentWindow;
7054     if (parent_window && parent_window != nav_window)
7055         parent_window->NavLastChildNavWindow = nav_window;
7056 }
7057 
7058 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)7059 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7060 {
7061     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7062 }
7063 
NavRestoreLayer(int layer)7064 static void NavRestoreLayer(int layer)
7065 {
7066     ImGuiContext& g = *GImGui;
7067     g.NavLayer = layer;
7068     if (layer == 0)
7069         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
7070     if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7071         ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
7072     else
7073         ImGui::NavInitWindow(g.NavWindow, true);
7074 }
7075 
NavUpdateAnyRequestFlag()7076 static inline void ImGui::NavUpdateAnyRequestFlag()
7077 {
7078     ImGuiContext& g = *GImGui;
7079     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7080     if (g.NavAnyRequest)
7081         IM_ASSERT(g.NavWindow != NULL);
7082 }
7083 
7084 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)7085 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7086 {
7087     ImGuiContext& g = *GImGui;
7088     IM_ASSERT(window == g.NavWindow);
7089     bool init_for_nav = false;
7090     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7091         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7092             init_for_nav = true;
7093     if (init_for_nav)
7094     {
7095         SetNavID(0, g.NavLayer);
7096         g.NavInitRequest = true;
7097         g.NavInitRequestFromMove = false;
7098         g.NavInitResultId = 0;
7099         g.NavInitResultRectRel = ImRect();
7100         NavUpdateAnyRequestFlag();
7101     }
7102     else
7103     {
7104         g.NavId = window->NavLastIds[0];
7105     }
7106 }
7107 
NavCalcPreferredRefPos()7108 static ImVec2 ImGui::NavCalcPreferredRefPos()
7109 {
7110     ImGuiContext& g = *GImGui;
7111     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7112     {
7113         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
7114         if (IsMousePosValid(&g.IO.MousePos))
7115             return g.IO.MousePos;
7116         return g.LastValidMousePos;
7117     }
7118     else
7119     {
7120         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
7121         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7122         ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
7123         ImRect visible_rect = GetViewportRect();
7124         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.
7125     }
7126 }
7127 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)7128 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7129 {
7130     ImGuiContext& g = *GImGui;
7131     if (mode == ImGuiInputReadMode_Down)
7132         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
7133 
7134     const float t = g.IO.NavInputsDownDuration[n];
7135     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
7136         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7137     if (t < 0.0f)
7138         return 0.0f;
7139     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
7140         return (t == 0.0f) ? 1.0f : 0.0f;
7141     if (mode == ImGuiInputReadMode_Repeat)
7142         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
7143     if (mode == ImGuiInputReadMode_RepeatSlow)
7144         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
7145     if (mode == ImGuiInputReadMode_RepeatFast)
7146         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
7147     return 0.0f;
7148 }
7149 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)7150 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7151 {
7152     ImVec2 delta(0.0f, 0.0f);
7153     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7154         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
7155     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7156         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
7157     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7158         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
7159     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
7160         delta *= slow_factor;
7161     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
7162         delta *= fast_factor;
7163     return delta;
7164 }
7165 
7166 // Scroll to keep newly navigated item fully into view
7167 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,const ImRect & item_rect)7168 static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7169 {
7170     ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7171     //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7172     if (window_rect.Contains(item_rect))
7173         return;
7174 
7175     ImGuiContext& g = *GImGui;
7176     if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7177     {
7178         window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7179         window->ScrollTargetCenterRatio.x = 0.0f;
7180     }
7181     else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7182     {
7183         window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7184         window->ScrollTargetCenterRatio.x = 1.0f;
7185     }
7186     if (item_rect.Min.y < window_rect.Min.y)
7187     {
7188         window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7189         window->ScrollTargetCenterRatio.y = 0.0f;
7190     }
7191     else if (item_rect.Max.y >= window_rect.Max.y)
7192     {
7193         window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7194         window->ScrollTargetCenterRatio.y = 1.0f;
7195     }
7196 }
7197 
NavUpdate()7198 static void ImGui::NavUpdate()
7199 {
7200     ImGuiContext& g = *GImGui;
7201     g.IO.WantSetMousePos = false;
7202 #if 0
7203     if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
7204 #endif
7205 
7206     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7207     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7208     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7209     if (nav_gamepad_active)
7210         if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
7211             g.NavInputSource = ImGuiInputSource_NavGamepad;
7212 
7213     // Update Keyboard->Nav inputs mapping
7214     if (nav_keyboard_active)
7215     {
7216         #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7217         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
7218         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
7219         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
7220         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7221         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7222         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
7223         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7224         if (g.IO.KeyCtrl)   g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7225         if (g.IO.KeyShift)  g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7226         if (g.IO.KeyAlt)    g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
7227         #undef NAV_MAP_KEY
7228     }
7229     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
7230     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7231         g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
7232 
7233     // Process navigation init request (select first/default focus)
7234     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7235     {
7236         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7237         IM_ASSERT(g.NavWindow);
7238         if (g.NavInitRequestFromMove)
7239             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
7240         else
7241             SetNavID(g.NavInitResultId, g.NavLayer);
7242         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7243     }
7244     g.NavInitRequest = false;
7245     g.NavInitRequestFromMove = false;
7246     g.NavInitResultId = 0;
7247     g.NavJustMovedToId = 0;
7248 
7249     // Process navigation move request
7250     if (g.NavMoveRequest)
7251         NavUpdateMoveResult();
7252 
7253     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7254     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7255     {
7256         IM_ASSERT(g.NavMoveRequest);
7257         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7258             g.NavDisableHighlight = false;
7259         g.NavMoveRequestForward = ImGuiNavForward_None;
7260     }
7261 
7262     // Apply application mouse position movement, after we had a chance to process move request result.
7263     if (g.NavMousePosDirty && g.NavIdIsAlive)
7264     {
7265         // Set mouse position given our knowledge of the navigated item position from last frame
7266         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7267         {
7268             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7269             {
7270                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7271                 g.IO.WantSetMousePos = true;
7272             }
7273         }
7274         g.NavMousePosDirty = false;
7275     }
7276     g.NavIdIsAlive = false;
7277     g.NavJustTabbedId = 0;
7278     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7279 
7280     // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
7281     if (g.NavWindow)
7282         NavSaveLastChildNavWindow(g.NavWindow);
7283     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7284         g.NavWindow->NavLastChildNavWindow = NULL;
7285 
7286     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7287     NavUpdateWindowing();
7288 
7289     // Set output flags for user application
7290     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7291     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
7292 
7293     // Process NavCancel input (to close a popup, get back to parent, clear focus)
7294     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
7295     {
7296         if (g.ActiveId != 0)
7297         {
7298             ClearActiveID();
7299         }
7300         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7301         {
7302             // Exit child window
7303             ImGuiWindow* child_window = g.NavWindow;
7304             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7305             IM_ASSERT(child_window->ChildId != 0);
7306             FocusWindow(parent_window);
7307             SetNavID(child_window->ChildId, 0);
7308             g.NavIdIsAlive = false;
7309             if (g.NavDisableMouseHover)
7310                 g.NavMousePosDirty = true;
7311         }
7312         else if (g.OpenPopupStack.Size > 0)
7313         {
7314             // Close open popup/menu
7315             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7316                 ClosePopupToLevel(g.OpenPopupStack.Size - 1);
7317         }
7318         else if (g.NavLayer != 0)
7319         {
7320             // Leave the "menu" layer
7321             NavRestoreLayer(0);
7322         }
7323         else
7324         {
7325             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7326             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7327                 g.NavWindow->NavLastIds[0] = 0;
7328             g.NavId = 0;
7329         }
7330     }
7331 
7332     // Process manual activation request
7333     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7334     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7335     {
7336         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
7337         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
7338         if (g.ActiveId == 0 && activate_pressed)
7339             g.NavActivateId = g.NavId;
7340         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7341             g.NavActivateDownId = g.NavId;
7342         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7343             g.NavActivatePressedId = g.NavId;
7344         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
7345             g.NavInputId = g.NavId;
7346     }
7347     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7348         g.NavDisableHighlight = true;
7349     if (g.NavActivateId != 0)
7350         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7351     g.NavMoveRequest = false;
7352 
7353     // Process programmatic activation request
7354     if (g.NavNextActivateId != 0)
7355         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7356     g.NavNextActivateId = 0;
7357 
7358     // Initiate directional inputs request
7359     const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7360     if (g.NavMoveRequestForward == ImGuiNavForward_None)
7361     {
7362         g.NavMoveDir = ImGuiDir_None;
7363         g.NavMoveRequestFlags = 0;
7364         if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7365         {
7366             if ((allowed_dir_flags & (1<<ImGuiDir_Left))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7367             if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7368             if ((allowed_dir_flags & (1<<ImGuiDir_Up))    && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp,   ImGuiNavInput_KeyUp_,   ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7369             if ((allowed_dir_flags & (1<<ImGuiDir_Down))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7370         }
7371         g.NavMoveClipDir = g.NavMoveDir;
7372     }
7373     else
7374     {
7375         // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
7376         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7377         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7378         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7379         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7380     }
7381 
7382     // Update PageUp/PageDown scroll
7383     float nav_scoring_rect_offset_y = 0.0f;
7384     if (nav_keyboard_active)
7385         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7386 
7387     // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
7388     if (g.NavMoveDir != ImGuiDir_None)
7389     {
7390         g.NavMoveRequest = true;
7391         g.NavMoveDirLast = g.NavMoveDir;
7392     }
7393     if (g.NavMoveRequest && g.NavId == 0)
7394     {
7395         g.NavInitRequest = g.NavInitRequestFromMove = true;
7396         g.NavInitResultId = 0;
7397         g.NavDisableHighlight = false;
7398     }
7399     NavUpdateAnyRequestFlag();
7400 
7401     // Scrolling
7402     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7403     {
7404         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7405         ImGuiWindow* window = g.NavWindow;
7406         const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
7407         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7408         {
7409             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7410                 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7411             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7412                 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7413         }
7414 
7415         // *Normal* Manual scroll with NavScrollXXX keys
7416         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7417         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
7418         if (scroll_dir.x != 0.0f && window->ScrollbarX)
7419         {
7420             SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
7421             g.NavMoveFromClampedRefRect = true;
7422         }
7423         if (scroll_dir.y != 0.0f)
7424         {
7425             SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
7426             g.NavMoveFromClampedRefRect = true;
7427         }
7428     }
7429 
7430     // Reset search results
7431     g.NavMoveResultLocal.Clear();
7432     g.NavMoveResultLocalVisibleSet.Clear();
7433     g.NavMoveResultOther.Clear();
7434 
7435     // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
7436     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7437     {
7438         ImGuiWindow* window = g.NavWindow;
7439         ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7440         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
7441         {
7442             float pad = window->CalcFontSize() * 0.5f;
7443             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
7444             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
7445             g.NavId = 0;
7446         }
7447         g.NavMoveFromClampedRefRect = false;
7448     }
7449 
7450     // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
7451     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7452     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7453     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
7454     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
7455     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7456     IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
7457     //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7458     g.NavScoringCount = 0;
7459 #if IMGUI_DEBUG_NAV_RECTS
7460     if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
7461     if (g.NavWindow) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
7462 #endif
7463 }
7464 
7465 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()7466 static void ImGui::NavUpdateMoveResult()
7467 {
7468     ImGuiContext& g = *GImGui;
7469     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7470     {
7471         // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
7472         if (g.NavId != 0)
7473         {
7474             g.NavDisableHighlight = false;
7475             g.NavDisableMouseHover = true;
7476         }
7477         return;
7478     }
7479 
7480     // Select which result to use
7481     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7482 
7483     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7484     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7485         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7486             result = &g.NavMoveResultLocalVisibleSet;
7487 
7488     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7489     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7490         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7491             result = &g.NavMoveResultOther;
7492     IM_ASSERT(g.NavWindow && result->Window);
7493 
7494     // Scroll to keep newly navigated item fully into view.
7495     if (g.NavLayer == 0)
7496     {
7497         ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7498         NavScrollToBringItemIntoView(result->Window, rect_abs);
7499 
7500         // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7501         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false);
7502         ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7503         result->RectRel.Translate(delta_scroll);
7504 
7505         // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7506         if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7507             NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7508     }
7509 
7510     ClearActiveID();
7511     g.NavWindow = result->Window;
7512     SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
7513     g.NavJustMovedToId = result->ID;
7514     g.NavMoveFromClampedRefRect = false;
7515 }
7516 
NavUpdatePageUpPageDown(int allowed_dir_flags)7517 static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7518 {
7519     ImGuiContext& g = *GImGui;
7520     if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7521     {
7522         ImGuiWindow* window = g.NavWindow;
7523         bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7524         bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7525         if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held))
7526         {
7527             if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7528             {
7529                 // Fallback manual-scroll when window has no navigable item
7530                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7531                     SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
7532                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7533                     SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
7534             }
7535             else
7536             {
7537                 const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7538                 const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7539                 float nav_scoring_rect_offset_y = 0.0f;
7540                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7541                 {
7542                     nav_scoring_rect_offset_y = -page_offset_y;
7543                     g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7544                     g.NavMoveClipDir = ImGuiDir_Up;
7545                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7546                 }
7547                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7548                 {
7549                     nav_scoring_rect_offset_y = +page_offset_y;
7550                     g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7551                     g.NavMoveClipDir = ImGuiDir_Down;
7552                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7553                 }
7554                 return nav_scoring_rect_offset_y;
7555             }
7556         }
7557     }
7558     return 0.0f;
7559 }
7560 
FindWindowFocusIndex(ImGuiWindow * window)7561 static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7562 {
7563     ImGuiContext& g = *GImGui;
7564     for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
7565         if (g.WindowsFocusOrder[i] == window)
7566             return i;
7567     return -1;
7568 }
7569 
FindWindowNavFocusable(int i_start,int i_stop,int dir)7570 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7571 {
7572     ImGuiContext& g = *GImGui;
7573     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
7574         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
7575             return g.WindowsFocusOrder[i];
7576     return NULL;
7577 }
7578 
NavUpdateWindowingHighlightWindow(int focus_change_dir)7579 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7580 {
7581     ImGuiContext& g = *GImGui;
7582     IM_ASSERT(g.NavWindowingTarget);
7583     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7584         return;
7585 
7586     const int i_current = FindWindowFocusIndex(g.NavWindowingTarget);
7587     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
7588     if (!window_target)
7589         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
7590     if (window_target) // Don't reset windowing target if there's a single window in the list
7591         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7592     g.NavWindowingToggleLayer = false;
7593 }
7594 
7595 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()7596 static void ImGui::NavUpdateWindowing()
7597 {
7598     ImGuiContext& g = *GImGui;
7599     ImGuiWindow* apply_focus_window = NULL;
7600     bool apply_toggle_layer = false;
7601 
7602     ImGuiWindow* modal_window = GetFrontMostPopupModal();
7603     if (modal_window != NULL)
7604     {
7605         g.NavWindowingTarget = NULL;
7606         return;
7607     }
7608 
7609     // Fade out
7610     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7611     {
7612         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
7613         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7614             g.NavWindowingTargetAnim = NULL;
7615     }
7616 
7617     // Start CTRL-TAB or Square+L/R window selection
7618     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
7619     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7620     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7621         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
7622         {
7623             g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
7624             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
7625             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
7626             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
7627         }
7628 
7629     // Gamepad update
7630     g.NavWindowingTimer += g.IO.DeltaTime;
7631     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
7632     {
7633         // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
7634         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
7635 
7636         // Select window to focus
7637         const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
7638         if (focus_change_dir != 0)
7639         {
7640             NavUpdateWindowingHighlightWindow(focus_change_dir);
7641             g.NavWindowingHighlightAlpha = 1.0f;
7642         }
7643 
7644         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
7645         if (!IsNavInputDown(ImGuiNavInput_Menu))
7646         {
7647             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
7648             if (g.NavWindowingToggleLayer && g.NavWindow)
7649                 apply_toggle_layer = true;
7650             else if (!g.NavWindowingToggleLayer)
7651                 apply_focus_window = g.NavWindowingTarget;
7652             g.NavWindowingTarget = NULL;
7653         }
7654     }
7655 
7656     // Keyboard: Focus
7657     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
7658     {
7659         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
7660         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
7661         if (IsKeyPressedMap(ImGuiKey_Tab, true))
7662             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
7663         if (!g.IO.KeyCtrl)
7664             apply_focus_window = g.NavWindowingTarget;
7665     }
7666 
7667     // Keyboard: Press and Release ALT to toggle menu layer
7668     // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
7669     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
7670         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
7671             apply_toggle_layer = true;
7672 
7673     // Move window
7674     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
7675     {
7676         ImVec2 move_delta;
7677         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
7678             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
7679         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
7680             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
7681         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
7682         {
7683             const float NAV_MOVE_SPEED = 800.0f;
7684             const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
7685             g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
7686             g.NavDisableMouseHover = true;
7687             MarkIniSettingsDirty(g.NavWindowingTarget);
7688         }
7689     }
7690 
7691     // Apply final focus
7692     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
7693     {
7694         g.NavDisableHighlight = false;
7695         g.NavDisableMouseHover = true;
7696         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
7697         ClosePopupsOverWindow(apply_focus_window);
7698         FocusWindow(apply_focus_window);
7699         if (apply_focus_window->NavLastIds[0] == 0)
7700             NavInitWindow(apply_focus_window, false);
7701 
7702         // If the window only has a menu layer, select it directly
7703         if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1))
7704             g.NavLayer = 1;
7705     }
7706     if (apply_focus_window)
7707         g.NavWindowingTarget = NULL;
7708 
7709     // Apply menu/layer toggle
7710     if (apply_toggle_layer && g.NavWindow)
7711     {
7712         // Move to parent menu if necessary
7713         ImGuiWindow* new_nav_window = g.NavWindow;
7714         while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7715             new_nav_window = new_nav_window->ParentWindow;
7716         if (new_nav_window != g.NavWindow)
7717         {
7718             ImGuiWindow* old_nav_window = g.NavWindow;
7719             FocusWindow(new_nav_window);
7720             new_nav_window->NavLastChildNavWindow = old_nav_window;
7721         }
7722         g.NavDisableHighlight = false;
7723         g.NavDisableMouseHover = true;
7724         NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0);
7725     }
7726 }
7727 
7728 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)7729 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
7730 {
7731     if (window->Flags & ImGuiWindowFlags_Popup)
7732         return "(Popup)";
7733     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
7734         return "(Main menu bar)";
7735     return "(Untitled)";
7736 }
7737 
7738 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingList()7739 void ImGui::NavUpdateWindowingList()
7740 {
7741     ImGuiContext& g = *GImGui;
7742     IM_ASSERT(g.NavWindowingTarget != NULL);
7743 
7744     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
7745         return;
7746 
7747     if (g.NavWindowingList == NULL)
7748         g.NavWindowingList = FindWindowByName("###NavWindowingList");
7749     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
7750     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
7751     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
7752     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
7753     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
7754     {
7755         ImGuiWindow* window = g.WindowsFocusOrder[n];
7756         if (!IsWindowNavFocusable(window))
7757             continue;
7758         const char* label = window->Name;
7759         if (label == FindRenderedTextEnd(label))
7760             label = GetFallbackWindowNameForWindowingList(window);
7761         Selectable(label, g.NavWindowingTarget == window);
7762     }
7763     End();
7764     PopStyleVar();
7765 }
7766 
7767 //-----------------------------------------------------------------------------
7768 // [SECTION] COLUMNS
7769 // In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
7770 //-----------------------------------------------------------------------------
7771 
NextColumn()7772 void ImGui::NextColumn()
7773 {
7774     ImGuiWindow* window = GetCurrentWindow();
7775     if (window->SkipItems || window->DC.ColumnsSet == NULL)
7776         return;
7777 
7778     ImGuiContext& g = *GImGui;
7779     PopItemWidth();
7780     PopClipRect();
7781 
7782     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7783     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
7784     if (++columns->Current < columns->Count)
7785     {
7786         // Columns 1+ cancel out IndentX
7787         window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
7788         window->DrawList->ChannelsSetCurrent(columns->Current);
7789     }
7790     else
7791     {
7792         window->DC.ColumnsOffset.x = 0.0f;
7793         window->DrawList->ChannelsSetCurrent(0);
7794         columns->Current = 0;
7795         columns->LineMinY = columns->LineMaxY;
7796     }
7797     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7798     window->DC.CursorPos.y = columns->LineMinY;
7799     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
7800     window->DC.CurrentLineTextBaseOffset = 0.0f;
7801 
7802     PushColumnClipRect();
7803     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
7804 }
7805 
GetColumnIndex()7806 int ImGui::GetColumnIndex()
7807 {
7808     ImGuiWindow* window = GetCurrentWindowRead();
7809     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
7810 }
7811 
GetColumnsCount()7812 int ImGui::GetColumnsCount()
7813 {
7814     ImGuiWindow* window = GetCurrentWindowRead();
7815     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
7816 }
7817 
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)7818 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
7819 {
7820     return offset_norm * (columns->MaxX - columns->MinX);
7821 }
7822 
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)7823 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
7824 {
7825     return offset / (columns->MaxX - columns->MinX);
7826 }
7827 
GetColumnsRectHalfWidth()7828 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
7829 
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)7830 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
7831 {
7832     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
7833     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
7834     ImGuiContext& g = *GImGui;
7835     ImGuiWindow* window = g.CurrentWindow;
7836     IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
7837     IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
7838 
7839     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
7840     x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
7841     if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
7842         x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
7843 
7844     return x;
7845 }
7846 
GetColumnOffset(int column_index)7847 float ImGui::GetColumnOffset(int column_index)
7848 {
7849     ImGuiWindow* window = GetCurrentWindowRead();
7850     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7851     IM_ASSERT(columns != NULL);
7852 
7853     if (column_index < 0)
7854         column_index = columns->Current;
7855     IM_ASSERT(column_index < columns->Columns.Size);
7856 
7857     const float t = columns->Columns[column_index].OffsetNorm;
7858     const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
7859     return x_offset;
7860 }
7861 
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)7862 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
7863 {
7864     if (column_index < 0)
7865         column_index = columns->Current;
7866 
7867     float offset_norm;
7868     if (before_resize)
7869         offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
7870     else
7871         offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
7872     return OffsetNormToPixels(columns, offset_norm);
7873 }
7874 
GetColumnWidth(int column_index)7875 float ImGui::GetColumnWidth(int column_index)
7876 {
7877     ImGuiWindow* window = GetCurrentWindowRead();
7878     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7879     IM_ASSERT(columns != NULL);
7880 
7881     if (column_index < 0)
7882         column_index = columns->Current;
7883     return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
7884 }
7885 
SetColumnOffset(int column_index,float offset)7886 void ImGui::SetColumnOffset(int column_index, float offset)
7887 {
7888     ImGuiContext& g = *GImGui;
7889     ImGuiWindow* window = g.CurrentWindow;
7890     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7891     IM_ASSERT(columns != NULL);
7892 
7893     if (column_index < 0)
7894         column_index = columns->Current;
7895     IM_ASSERT(column_index < columns->Columns.Size);
7896 
7897     const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
7898     const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
7899 
7900     if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
7901         offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
7902     columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
7903 
7904     if (preserve_width)
7905         SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
7906 }
7907 
SetColumnWidth(int column_index,float width)7908 void ImGui::SetColumnWidth(int column_index, float width)
7909 {
7910     ImGuiWindow* window = GetCurrentWindowRead();
7911     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7912     IM_ASSERT(columns != NULL);
7913 
7914     if (column_index < 0)
7915         column_index = columns->Current;
7916     SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
7917 }
7918 
PushColumnClipRect(int column_index)7919 void ImGui::PushColumnClipRect(int column_index)
7920 {
7921     ImGuiWindow* window = GetCurrentWindowRead();
7922     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7923     if (column_index < 0)
7924         column_index = columns->Current;
7925 
7926     PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
7927 }
7928 
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)7929 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
7930 {
7931     for (int n = 0; n < window->ColumnsStorage.Size; n++)
7932         if (window->ColumnsStorage[n].ID == id)
7933             return &window->ColumnsStorage[n];
7934 
7935     window->ColumnsStorage.push_back(ImGuiColumnsSet());
7936     ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
7937     columns->ID = id;
7938     return columns;
7939 }
7940 
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)7941 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
7942 {
7943     ImGuiContext& g = *GImGui;
7944     ImGuiWindow* window = GetCurrentWindow();
7945 
7946     IM_ASSERT(columns_count > 1);
7947     IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
7948 
7949     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
7950     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
7951     PushID(0x11223347 + (str_id ? 0 : columns_count));
7952     ImGuiID id = window->GetID(str_id ? str_id : "columns");
7953     PopID();
7954 
7955     // Acquire storage for the columns set
7956     ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
7957     IM_ASSERT(columns->ID == id);
7958     columns->Current = 0;
7959     columns->Count = columns_count;
7960     columns->Flags = flags;
7961     window->DC.ColumnsSet = columns;
7962 
7963     // Set state for first column
7964     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
7965     columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
7966     columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
7967     columns->StartPosY = window->DC.CursorPos.y;
7968     columns->StartMaxPosX = window->DC.CursorMaxPos.x;
7969     columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
7970     window->DC.ColumnsOffset.x = 0.0f;
7971     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7972 
7973     // Clear data if columns count changed
7974     if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
7975         columns->Columns.resize(0);
7976 
7977     // Initialize defaults
7978     columns->IsFirstFrame = (columns->Columns.Size == 0);
7979     if (columns->Columns.Size == 0)
7980     {
7981         columns->Columns.reserve(columns_count + 1);
7982         for (int n = 0; n < columns_count + 1; n++)
7983         {
7984             ImGuiColumnData column;
7985             column.OffsetNorm = n / (float)columns_count;
7986             columns->Columns.push_back(column);
7987         }
7988     }
7989 
7990     for (int n = 0; n < columns_count; n++)
7991     {
7992         // Compute clipping rectangle
7993         ImGuiColumnData* column = &columns->Columns[n];
7994         float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
7995         float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
7996         column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
7997         column->ClipRect.ClipWith(window->ClipRect);
7998     }
7999 
8000     window->DrawList->ChannelsSplit(columns->Count);
8001     PushColumnClipRect();
8002     PushItemWidth(GetColumnWidth() * 0.65f);
8003 }
8004 
EndColumns()8005 void ImGui::EndColumns()
8006 {
8007     ImGuiContext& g = *GImGui;
8008     ImGuiWindow* window = GetCurrentWindow();
8009     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8010     IM_ASSERT(columns != NULL);
8011 
8012     PopItemWidth();
8013     PopClipRect();
8014     window->DrawList->ChannelsMerge();
8015 
8016     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8017     window->DC.CursorPos.y = columns->LineMaxY;
8018     if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
8019         window->DC.CursorMaxPos.x = columns->StartMaxPosX;  // Restore cursor max pos, as columns don't grow parent
8020 
8021     // Draw columns borders and handle resize
8022     bool is_being_resized = false;
8023     if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
8024     {
8025         const float y1 = columns->StartPosY;
8026         const float y2 = window->DC.CursorPos.y;
8027         int dragging_column = -1;
8028         for (int n = 1; n < columns->Count; n++)
8029         {
8030             float x = window->Pos.x + GetColumnOffset(n);
8031             const ImGuiID column_id = columns->ID + ImGuiID(n);
8032             const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
8033             const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
8034             KeepAliveID(column_id);
8035             if (IsClippedEx(column_rect, column_id, false))
8036                 continue;
8037 
8038             bool hovered = false, held = false;
8039             if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
8040             {
8041                 ButtonBehavior(column_rect, column_id, &hovered, &held);
8042                 if (hovered || held)
8043                     g.MouseCursor = ImGuiMouseCursor_ResizeEW;
8044                 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
8045                     dragging_column = n;
8046             }
8047 
8048             // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
8049             const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
8050             const float xi = (float)(int)x;
8051             window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
8052         }
8053 
8054         // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
8055         if (dragging_column != -1)
8056         {
8057             if (!columns->IsBeingResized)
8058                 for (int n = 0; n < columns->Count + 1; n++)
8059                     columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
8060             columns->IsBeingResized = is_being_resized = true;
8061             float x = GetDraggedColumnOffset(columns, dragging_column);
8062             SetColumnOffset(dragging_column, x);
8063         }
8064     }
8065     columns->IsBeingResized = is_being_resized;
8066 
8067     window->DC.ColumnsSet = NULL;
8068     window->DC.ColumnsOffset.x = 0.0f;
8069     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8070 }
8071 
8072 // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
Columns(int columns_count,const char * id,bool border)8073 void ImGui::Columns(int columns_count, const char* id, bool border)
8074 {
8075     ImGuiWindow* window = GetCurrentWindow();
8076     IM_ASSERT(columns_count >= 1);
8077 
8078     ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
8079     //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
8080     if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
8081         return;
8082 
8083     if (window->DC.ColumnsSet != NULL)
8084         EndColumns();
8085 
8086     if (columns_count != 1)
8087         BeginColumns(id, columns_count, flags);
8088 }
8089 
8090 //-----------------------------------------------------------------------------
8091 // [SECTION] DRAG AND DROP
8092 //-----------------------------------------------------------------------------
8093 
ClearDragDrop()8094 void ImGui::ClearDragDrop()
8095 {
8096     ImGuiContext& g = *GImGui;
8097     g.DragDropActive = false;
8098     g.DragDropPayload.Clear();
8099     g.DragDropAcceptFlags = 0;
8100     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8101     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8102     g.DragDropAcceptFrameCount = -1;
8103 
8104     g.DragDropPayloadBufHeap.clear();
8105     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8106 }
8107 
8108 // Call when current ID is active.
8109 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
BeginDragDropSource(ImGuiDragDropFlags flags)8110 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8111 {
8112     ImGuiContext& g = *GImGui;
8113     ImGuiWindow* window = g.CurrentWindow;
8114 
8115     bool source_drag_active = false;
8116     ImGuiID source_id = 0;
8117     ImGuiID source_parent_id = 0;
8118     int mouse_button = 0;
8119     if (!(flags & ImGuiDragDropFlags_SourceExtern))
8120     {
8121         source_id = window->DC.LastItemId;
8122         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8123             return false;
8124         if (g.IO.MouseDown[mouse_button] == false)
8125             return false;
8126 
8127         if (source_id == 0)
8128         {
8129             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8130             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8131             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8132             {
8133                 IM_ASSERT(0);
8134                 return false;
8135             }
8136 
8137             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8138             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8139             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8140             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8141             bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8142             if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8143                 return false;
8144             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8145             if (is_hovered)
8146                 SetHoveredID(source_id);
8147             if (is_hovered && g.IO.MouseClicked[mouse_button])
8148             {
8149                 SetActiveID(source_id, window);
8150                 FocusWindow(window);
8151             }
8152             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8153                 g.ActiveIdAllowOverlap = is_hovered;
8154         }
8155         else
8156         {
8157             g.ActiveIdAllowOverlap = false;
8158         }
8159         if (g.ActiveId != source_id)
8160             return false;
8161         source_parent_id = window->IDStack.back();
8162         source_drag_active = IsMouseDragging(mouse_button);
8163     }
8164     else
8165     {
8166         window = NULL;
8167         source_id = ImHash("#SourceExtern", 0);
8168         source_drag_active = true;
8169     }
8170 
8171     if (source_drag_active)
8172     {
8173         if (!g.DragDropActive)
8174         {
8175             IM_ASSERT(source_id != 0);
8176             ClearDragDrop();
8177             ImGuiPayload& payload = g.DragDropPayload;
8178             payload.SourceId = source_id;
8179             payload.SourceParentId = source_parent_id;
8180             g.DragDropActive = true;
8181             g.DragDropSourceFlags = flags;
8182             g.DragDropMouseButton = mouse_button;
8183         }
8184         g.DragDropSourceFrameCount = g.FrameCount;
8185         g.DragDropWithinSourceOrTarget = true;
8186 
8187         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8188         {
8189             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8190             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8191             BeginTooltip();
8192             if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8193             {
8194                 ImGuiWindow* tooltip_window = g.CurrentWindow;
8195                 tooltip_window->SkipItems = true;
8196                 tooltip_window->HiddenFramesRegular = 1;
8197             }
8198         }
8199 
8200         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8201             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8202 
8203         return true;
8204     }
8205     return false;
8206 }
8207 
EndDragDropSource()8208 void ImGui::EndDragDropSource()
8209 {
8210     ImGuiContext& g = *GImGui;
8211     IM_ASSERT(g.DragDropActive);
8212     IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8213 
8214     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8215         EndTooltip();
8216 
8217     // Discard the drag if have not called SetDragDropPayload()
8218     if (g.DragDropPayload.DataFrameCount == -1)
8219         ClearDragDrop();
8220     g.DragDropWithinSourceOrTarget = false;
8221 }
8222 
8223 // Use 'cond' to choose to submit payload on drag start or every frame
SetDragDropPayload(const char * type,const void * data,size_t data_size,ImGuiCond cond)8224 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8225 {
8226     ImGuiContext& g = *GImGui;
8227     ImGuiPayload& payload = g.DragDropPayload;
8228     if (cond == 0)
8229         cond = ImGuiCond_Always;
8230 
8231     IM_ASSERT(type != NULL);
8232     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8233     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8234     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8235     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
8236 
8237     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8238     {
8239         // Copy payload
8240         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8241         g.DragDropPayloadBufHeap.resize(0);
8242         if (data_size > sizeof(g.DragDropPayloadBufLocal))
8243         {
8244             // Store in heap
8245             g.DragDropPayloadBufHeap.resize((int)data_size);
8246             payload.Data = g.DragDropPayloadBufHeap.Data;
8247             memcpy(payload.Data, data, data_size);
8248         }
8249         else if (data_size > 0)
8250         {
8251             // Store locally
8252             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8253             payload.Data = g.DragDropPayloadBufLocal;
8254             memcpy(payload.Data, data, data_size);
8255         }
8256         else
8257         {
8258             payload.Data = NULL;
8259         }
8260         payload.DataSize = (int)data_size;
8261     }
8262     payload.DataFrameCount = g.FrameCount;
8263 
8264     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8265 }
8266 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8267 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8268 {
8269     ImGuiContext& g = *GImGui;
8270     if (!g.DragDropActive)
8271         return false;
8272 
8273     ImGuiWindow* window = g.CurrentWindow;
8274     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8275         return false;
8276     IM_ASSERT(id != 0);
8277     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8278         return false;
8279     if (window->SkipItems)
8280         return false;
8281 
8282     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8283     g.DragDropTargetRect = bb;
8284     g.DragDropTargetId = id;
8285     g.DragDropWithinSourceOrTarget = true;
8286     return true;
8287 }
8288 
8289 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8290 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8291 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8292 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8293 bool ImGui::BeginDragDropTarget()
8294 {
8295     ImGuiContext& g = *GImGui;
8296     if (!g.DragDropActive)
8297         return false;
8298 
8299     ImGuiWindow* window = g.CurrentWindow;
8300     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8301         return false;
8302     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8303         return false;
8304 
8305     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8306     ImGuiID id = window->DC.LastItemId;
8307     if (id == 0)
8308         id = window->GetIDFromRectangle(display_rect);
8309     if (g.DragDropPayload.SourceId == id)
8310         return false;
8311 
8312     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8313     g.DragDropTargetRect = display_rect;
8314     g.DragDropTargetId = id;
8315     g.DragDropWithinSourceOrTarget = true;
8316     return true;
8317 }
8318 
IsDragDropPayloadBeingAccepted()8319 bool ImGui::IsDragDropPayloadBeingAccepted()
8320 {
8321     ImGuiContext& g = *GImGui;
8322     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8323 }
8324 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8325 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8326 {
8327     ImGuiContext& g = *GImGui;
8328     ImGuiWindow* window = g.CurrentWindow;
8329     ImGuiPayload& payload = g.DragDropPayload;
8330     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8331     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
8332     if (type != NULL && !payload.IsDataType(type))
8333         return NULL;
8334 
8335     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8336     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8337     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8338     ImRect r = g.DragDropTargetRect;
8339     float r_surface = r.GetWidth() * r.GetHeight();
8340     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8341     {
8342         g.DragDropAcceptFlags = flags;
8343         g.DragDropAcceptIdCurr = g.DragDropTargetId;
8344         g.DragDropAcceptIdCurrRectSurface = r_surface;
8345     }
8346 
8347     // Render default drop visuals
8348     payload.Preview = was_accepted_previously;
8349     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8350     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8351     {
8352         // FIXME-DRAG: Settle on a proper default visuals for drop target.
8353         r.Expand(3.5f);
8354         bool push_clip_rect = !window->ClipRect.Contains(r);
8355         if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8356         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8357         if (push_clip_rect) window->DrawList->PopClipRect();
8358     }
8359 
8360     g.DragDropAcceptFrameCount = g.FrameCount;
8361     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()
8362     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8363         return NULL;
8364 
8365     return &payload;
8366 }
8367 
GetDragDropPayload()8368 const ImGuiPayload* ImGui::GetDragDropPayload()
8369 {
8370     ImGuiContext& g = *GImGui;
8371     return g.DragDropActive ? &g.DragDropPayload : NULL;
8372 }
8373 
8374 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8375 void ImGui::EndDragDropTarget()
8376 {
8377     ImGuiContext& g = *GImGui;
8378     IM_ASSERT(g.DragDropActive);
8379     IM_ASSERT(g.DragDropWithinSourceOrTarget);
8380     g.DragDropWithinSourceOrTarget = false;
8381 }
8382 
8383 //-----------------------------------------------------------------------------
8384 // [SECTION] LOGGING/CAPTURING
8385 //-----------------------------------------------------------------------------
8386 
8387 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8388 void ImGui::LogText(const char* fmt, ...)
8389 {
8390     ImGuiContext& g = *GImGui;
8391     if (!g.LogEnabled)
8392         return;
8393 
8394     va_list args;
8395     va_start(args, fmt);
8396     if (g.LogFile)
8397         vfprintf(g.LogFile, fmt, args);
8398     else
8399         g.LogClipboard.appendfv(fmt, args);
8400     va_end(args);
8401 }
8402 
8403 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
8404 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)8405 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8406 {
8407     ImGuiContext& g = *GImGui;
8408     ImGuiWindow* window = g.CurrentWindow;
8409 
8410     if (!text_end)
8411         text_end = FindRenderedTextEnd(text, text_end);
8412 
8413     const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8414     if (ref_pos)
8415         window->DC.LogLinePosY = ref_pos->y;
8416 
8417     const char* text_remaining = text;
8418     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
8419         g.LogStartDepth = window->DC.TreeDepth;
8420     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8421     for (;;)
8422     {
8423         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8424         const char* line_start = text_remaining;
8425         const char* line_end = ImStreolRange(line_start, text_end);
8426         const bool is_first_line = (line_start == text);
8427         const bool is_last_line = (line_end == text_end);
8428         if (!is_last_line || (line_start != line_end))
8429         {
8430             const int char_count = (int)(line_end - line_start);
8431             if (log_new_line || !is_first_line)
8432                 LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start);
8433             else
8434                 LogText(" %.*s", char_count, line_start);
8435         }
8436 
8437         if (is_last_line)
8438             break;
8439         text_remaining = line_end + 1;
8440     }
8441 }
8442 
8443 // Start logging ImGui output to TTY
LogToTTY(int max_depth)8444 void ImGui::LogToTTY(int max_depth)
8445 {
8446     ImGuiContext& g = *GImGui;
8447     if (g.LogEnabled)
8448         return;
8449     ImGuiWindow* window = g.CurrentWindow;
8450 
8451     IM_ASSERT(g.LogFile == NULL);
8452     g.LogFile = stdout;
8453     g.LogEnabled = true;
8454     g.LogStartDepth = window->DC.TreeDepth;
8455     if (max_depth >= 0)
8456         g.LogAutoExpandMaxDepth = max_depth;
8457 }
8458 
8459 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)8460 void ImGui::LogToFile(int max_depth, const char* filename)
8461 {
8462     ImGuiContext& g = *GImGui;
8463     if (g.LogEnabled)
8464         return;
8465     ImGuiWindow* window = g.CurrentWindow;
8466 
8467     if (!filename)
8468     {
8469         filename = g.IO.LogFilename;
8470         if (!filename)
8471             return;
8472     }
8473 
8474     IM_ASSERT(g.LogFile == NULL);
8475     g.LogFile = ImFileOpen(filename, "ab");
8476     if (!g.LogFile)
8477     {
8478         IM_ASSERT(g.LogFile != NULL); // Consider this an error
8479         return;
8480     }
8481     g.LogEnabled = true;
8482     g.LogStartDepth = window->DC.TreeDepth;
8483     if (max_depth >= 0)
8484         g.LogAutoExpandMaxDepth = max_depth;
8485 }
8486 
8487 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)8488 void ImGui::LogToClipboard(int max_depth)
8489 {
8490     ImGuiContext& g = *GImGui;
8491     if (g.LogEnabled)
8492         return;
8493     ImGuiWindow* window = g.CurrentWindow;
8494 
8495     IM_ASSERT(g.LogFile == NULL);
8496     g.LogFile = NULL;
8497     g.LogEnabled = true;
8498     g.LogStartDepth = window->DC.TreeDepth;
8499     if (max_depth >= 0)
8500         g.LogAutoExpandMaxDepth = max_depth;
8501 }
8502 
LogFinish()8503 void ImGui::LogFinish()
8504 {
8505     ImGuiContext& g = *GImGui;
8506     if (!g.LogEnabled)
8507         return;
8508 
8509     LogText(IM_NEWLINE);
8510     if (g.LogFile != NULL)
8511     {
8512         if (g.LogFile == stdout)
8513             fflush(g.LogFile);
8514         else
8515             fclose(g.LogFile);
8516         g.LogFile = NULL;
8517     }
8518     if (g.LogClipboard.size() > 1)
8519     {
8520         SetClipboardText(g.LogClipboard.begin());
8521         g.LogClipboard.clear();
8522     }
8523     g.LogEnabled = false;
8524 }
8525 
8526 // Helper to display logging buttons
LogButtons()8527 void ImGui::LogButtons()
8528 {
8529     ImGuiContext& g = *GImGui;
8530 
8531     PushID("LogButtons");
8532     const bool log_to_tty = Button("Log To TTY"); SameLine();
8533     const bool log_to_file = Button("Log To File"); SameLine();
8534     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
8535     PushItemWidth(80.0f);
8536     PushAllowKeyboardFocus(false);
8537     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8538     PopAllowKeyboardFocus();
8539     PopItemWidth();
8540     PopID();
8541 
8542     // Start logging at the end of the function so that the buttons don't appear in the log
8543     if (log_to_tty)
8544         LogToTTY(g.LogAutoExpandMaxDepth);
8545     if (log_to_file)
8546         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8547     if (log_to_clipboard)
8548         LogToClipboard(g.LogAutoExpandMaxDepth);
8549 }
8550 
8551 //-----------------------------------------------------------------------------
8552 // [SECTION] SETTINGS
8553 //-----------------------------------------------------------------------------
8554 
MarkIniSettingsDirty()8555 void ImGui::MarkIniSettingsDirty()
8556 {
8557     ImGuiContext& g = *GImGui;
8558     if (g.SettingsDirtyTimer <= 0.0f)
8559         g.SettingsDirtyTimer = g.IO.IniSavingRate;
8560 }
8561 
MarkIniSettingsDirty(ImGuiWindow * window)8562 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8563 {
8564     ImGuiContext& g = *GImGui;
8565     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8566         if (g.SettingsDirtyTimer <= 0.0f)
8567             g.SettingsDirtyTimer = g.IO.IniSavingRate;
8568 }
8569 
CreateNewWindowSettings(const char * name)8570 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
8571 {
8572     ImGuiContext& g = *GImGui;
8573     g.SettingsWindows.push_back(ImGuiWindowSettings());
8574     ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8575     settings->Name = ImStrdup(name);
8576     settings->ID = ImHash(name, 0);
8577     return settings;
8578 }
8579 
FindWindowSettings(ImGuiID id)8580 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8581 {
8582     ImGuiContext& g = *GImGui;
8583     for (int i = 0; i != g.SettingsWindows.Size; i++)
8584         if (g.SettingsWindows[i].ID == id)
8585             return &g.SettingsWindows[i];
8586     return NULL;
8587 }
8588 
LoadIniSettingsFromDisk(const char * ini_filename)8589 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8590 {
8591     size_t file_data_size = 0;
8592     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
8593     if (!file_data)
8594         return;
8595     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
8596     ImGui::MemFree(file_data);
8597 }
8598 
FindSettingsHandler(const char * type_name)8599 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8600 {
8601     ImGuiContext& g = *GImGui;
8602     const ImGuiID type_hash = ImHash(type_name, 0, 0);
8603     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8604         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
8605             return &g.SettingsHandlers[handler_n];
8606     return NULL;
8607 }
8608 
8609 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)8610 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
8611 {
8612     ImGuiContext& g = *GImGui;
8613     IM_ASSERT(g.Initialized);
8614     IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
8615 
8616     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
8617     // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
8618     if (ini_size == 0)
8619         ini_size = strlen(ini_data);
8620     char* buf = (char*)ImGui::MemAlloc(ini_size + 1);
8621     char* buf_end = buf + ini_size;
8622     memcpy(buf, ini_data, ini_size);
8623     buf[ini_size] = 0;
8624 
8625     void* entry_data = NULL;
8626     ImGuiSettingsHandler* entry_handler = NULL;
8627 
8628     char* line_end = NULL;
8629     for (char* line = buf; line < buf_end; line = line_end + 1)
8630     {
8631         // Skip new lines markers, then find end of the line
8632         while (*line == '\n' || *line == '\r')
8633             line++;
8634         line_end = line;
8635         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
8636             line_end++;
8637         line_end[0] = 0;
8638         if (line[0] == ';')
8639             continue;
8640         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
8641         {
8642             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
8643             line_end[-1] = 0;
8644             const char* name_end = line_end - 1;
8645             const char* type_start = line + 1;
8646             char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
8647             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
8648             if (!type_end || !name_start)
8649             {
8650                 name_start = type_start; // Import legacy entries that have no type
8651                 type_start = "Window";
8652             }
8653             else
8654             {
8655                 *type_end = 0; // Overwrite first ']'
8656                 name_start++;  // Skip second '['
8657             }
8658             entry_handler = FindSettingsHandler(type_start);
8659             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
8660         }
8661         else if (entry_handler != NULL && entry_data != NULL)
8662         {
8663             // Let type handler parse the line
8664             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
8665         }
8666     }
8667     ImGui::MemFree(buf);
8668     g.SettingsLoaded = true;
8669 }
8670 
SaveIniSettingsToDisk(const char * ini_filename)8671 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
8672 {
8673     ImGuiContext& g = *GImGui;
8674     g.SettingsDirtyTimer = 0.0f;
8675     if (!ini_filename)
8676         return;
8677 
8678     size_t ini_data_size = 0;
8679     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
8680     FILE* f = ImFileOpen(ini_filename, "wt");
8681     if (!f)
8682         return;
8683     fwrite(ini_data, sizeof(char), ini_data_size, f);
8684     fclose(f);
8685 }
8686 
8687 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)8688 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
8689 {
8690     ImGuiContext& g = *GImGui;
8691     g.SettingsDirtyTimer = 0.0f;
8692     g.SettingsIniData.Buf.resize(0);
8693     g.SettingsIniData.Buf.push_back(0);
8694     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8695     {
8696         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
8697         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
8698     }
8699     if (out_size)
8700         *out_size = (size_t)g.SettingsIniData.size();
8701     return g.SettingsIniData.c_str();
8702 }
8703 
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)8704 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
8705 {
8706     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
8707     if (!settings)
8708         settings = ImGui::CreateNewWindowSettings(name);
8709     return (void*)settings;
8710 }
8711 
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)8712 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
8713 {
8714     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
8715     float x, y;
8716     int i;
8717     if (sscanf(line, "Pos=%f,%f", &x, &y) == 2)         settings->Pos = ImVec2(x, y);
8718     else if (sscanf(line, "Size=%f,%f", &x, &y) == 2)   settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
8719     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
8720 }
8721 
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)8722 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
8723 {
8724     // Gather data from windows that were active during this session
8725     // (if a window wasn't opened in this session we preserve its settings)
8726     ImGuiContext& g = *imgui_ctx;
8727     for (int i = 0; i != g.Windows.Size; i++)
8728     {
8729         ImGuiWindow* window = g.Windows[i];
8730         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
8731             continue;
8732 
8733         ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
8734         if (!settings)
8735         {
8736             settings = ImGui::CreateNewWindowSettings(window->Name);
8737             window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
8738         }
8739         IM_ASSERT(settings->ID == window->ID);
8740         settings->Pos = window->Pos;
8741         settings->Size = window->SizeFull;
8742         settings->Collapsed = window->Collapsed;
8743     }
8744 
8745     // Write to text buffer
8746     buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
8747     for (int i = 0; i != g.SettingsWindows.Size; i++)
8748     {
8749         const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
8750         if (settings->Pos.x == FLT_MAX)
8751             continue;
8752         const char* name = settings->Name;
8753         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
8754             name = p;
8755         buf->appendf("[%s][%s]\n", handler->TypeName, name);
8756         buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
8757         buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
8758         buf->appendf("Collapsed=%d\n", settings->Collapsed);
8759         buf->appendf("\n");
8760     }
8761 }
8762 
8763 //-----------------------------------------------------------------------------
8764 // [SECTION] PLATFORM DEPENDENT HELPERS
8765 //-----------------------------------------------------------------------------
8766 
8767 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
8768 #ifndef WIN32_LEAN_AND_MEAN
8769 #define WIN32_LEAN_AND_MEAN
8770 #endif
8771 #ifndef __MINGW32__
8772 #include <Windows.h>
8773 #else
8774 #include <windows.h>
8775 #endif
8776 #endif
8777 
8778 // Win32 API clipboard implementation
8779 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
8780 
8781 #ifdef _MSC_VER
8782 #pragma comment(lib, "user32")
8783 #endif
8784 
GetClipboardTextFn_DefaultImpl(void *)8785 static const char* GetClipboardTextFn_DefaultImpl(void*)
8786 {
8787     static ImVector<char> buf_local;
8788     buf_local.clear();
8789     if (!::OpenClipboard(NULL))
8790         return NULL;
8791     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
8792     if (wbuf_handle == NULL)
8793     {
8794         ::CloseClipboard();
8795         return NULL;
8796     }
8797     if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
8798     {
8799         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
8800         buf_local.resize(buf_len);
8801         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
8802     }
8803     ::GlobalUnlock(wbuf_handle);
8804     ::CloseClipboard();
8805     return buf_local.Data;
8806 }
8807 
SetClipboardTextFn_DefaultImpl(void *,const char * text)8808 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8809 {
8810     if (!::OpenClipboard(NULL))
8811         return;
8812     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
8813     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
8814     if (wbuf_handle == NULL)
8815     {
8816         ::CloseClipboard();
8817         return;
8818     }
8819     ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
8820     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
8821     ::GlobalUnlock(wbuf_handle);
8822     ::EmptyClipboard();
8823     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
8824         ::GlobalFree(wbuf_handle);
8825     ::CloseClipboard();
8826 }
8827 
8828 #else
8829 
8830 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)8831 static const char* GetClipboardTextFn_DefaultImpl(void*)
8832 {
8833     ImGuiContext& g = *GImGui;
8834     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
8835 }
8836 
8837 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)8838 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8839 {
8840     ImGuiContext& g = *GImGui;
8841     g.PrivateClipboard.clear();
8842     const char* text_end = text + strlen(text);
8843     g.PrivateClipboard.resize((int)(text_end - text) + 1);
8844     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
8845     g.PrivateClipboard[(int)(text_end - text)] = 0;
8846 }
8847 
8848 #endif
8849 
8850 // Win32 API IME support (for Asian languages, etc.)
8851 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
8852 
8853 #include <imm.h>
8854 #ifdef _MSC_VER
8855 #pragma comment(lib, "imm32")
8856 #endif
8857 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)8858 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
8859 {
8860     // Notify OS Input Method Editor of text input position
8861     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
8862         if (HIMC himc = ::ImmGetContext(hwnd))
8863         {
8864             COMPOSITIONFORM cf;
8865             cf.ptCurrentPos.x = x;
8866             cf.ptCurrentPos.y = y;
8867             cf.dwStyle = CFS_FORCE_POSITION;
8868             ::ImmSetCompositionWindow(himc, &cf);
8869             ::ImmReleaseContext(hwnd, himc);
8870         }
8871 }
8872 
8873 #else
8874 
ImeSetInputScreenPosFn_DefaultImpl(int,int)8875 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
8876 
8877 #endif
8878 
8879 //-----------------------------------------------------------------------------
8880 // [SECTION] METRICS/DEBUG WINDOW
8881 //-----------------------------------------------------------------------------
8882 
ShowMetricsWindow(bool * p_open)8883 void ImGui::ShowMetricsWindow(bool* p_open)
8884 {
8885     if (!ImGui::Begin("ImGui Metrics", p_open))
8886     {
8887         ImGui::End();
8888         return;
8889     }
8890     static bool show_draw_cmd_clip_rects = true;
8891     static bool show_window_begin_order = false;
8892     ImGuiIO& io = ImGui::GetIO();
8893     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
8894     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
8895     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
8896     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
8897     ImGui::Text("%d allocations", io.MetricsActiveAllocations);
8898     ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
8899     ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order);
8900 
8901     ImGui::Separator();
8902 
8903     struct Funcs
8904     {
8905         static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
8906         {
8907             bool node_open = ImGui::TreeNode(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);
8908             if (draw_list == ImGui::GetWindowDrawList())
8909             {
8910                 ImGui::SameLine();
8911                 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
8912                 if (node_open) ImGui::TreePop();
8913                 return;
8914             }
8915 
8916             ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list
8917             if (window && IsItemHovered())
8918                 overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
8919             if (!node_open)
8920                 return;
8921 
8922             int elem_offset = 0;
8923             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
8924             {
8925                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
8926                     continue;
8927                 if (pcmd->UserCallback)
8928                 {
8929                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
8930                     continue;
8931                 }
8932                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
8933                 bool pcmd_node_open = ImGui::TreeNode((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);
8934                 if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
8935                 {
8936                     ImRect clip_rect = pcmd->ClipRect;
8937                     ImRect vtxs_rect;
8938                     for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
8939                         vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
8940                     clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
8941                     vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
8942                 }
8943                 if (!pcmd_node_open)
8944                     continue;
8945 
8946                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
8947                 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
8948                 while (clipper.Step())
8949                     for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
8950                     {
8951                         char buf[300];
8952                         char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
8953                         ImVec2 triangles_pos[3];
8954                         for (int n = 0; n < 3; n++, vtx_i++)
8955                         {
8956                             ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
8957                             triangles_pos[n] = v.pos;
8958                             buf_p += ImFormatString(buf_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);
8959                         }
8960                         ImGui::Selectable(buf, false);
8961                         if (ImGui::IsItemHovered())
8962                         {
8963                             ImDrawListFlags backup_flags = overlay_draw_list->Flags;
8964                             overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
8965                             overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
8966                             overlay_draw_list->Flags = backup_flags;
8967                         }
8968                     }
8969                 ImGui::TreePop();
8970             }
8971             ImGui::TreePop();
8972         }
8973 
8974         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
8975         {
8976             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
8977                 return;
8978             for (int i = 0; i < windows.Size; i++)
8979                 Funcs::NodeWindow(windows[i], "Window");
8980             ImGui::TreePop();
8981         }
8982 
8983         static void NodeWindow(ImGuiWindow* window, const char* label)
8984         {
8985             if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
8986                 return;
8987             ImGuiWindowFlags flags = window->Flags;
8988             NodeDrawList(window, window->DrawList, "DrawList");
8989             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);
8990             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
8991                 (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
8992                 (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
8993                 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
8994             ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetWindowScrollMaxX(window), window->Scroll.y, GetWindowScrollMaxY(window));
8995             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
8996             ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
8997             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
8998             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
8999             if (!window->NavRectRel[0].IsInverted())
9000                 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);
9001             else
9002                 ImGui::BulletText("NavRectRel[0]: <None>");
9003             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9004             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9005             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9006             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9007             {
9008                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9009                 {
9010                     const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
9011                     if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9012                     {
9013                         ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
9014                         for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9015                             ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
9016                         ImGui::TreePop();
9017                     }
9018                 }
9019                 ImGui::TreePop();
9020             }
9021             ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9022             ImGui::TreePop();
9023         }
9024     };
9025 
9026     // Access private state, we are going to display the draw lists from last frame
9027     ImGuiContext& g = *GImGui;
9028     Funcs::NodeWindows(g.Windows, "Windows");
9029     if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9030     {
9031         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9032             Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9033         ImGui::TreePop();
9034     }
9035     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9036     {
9037         for (int i = 0; i < g.OpenPopupStack.Size; i++)
9038         {
9039             ImGuiWindow* window = g.OpenPopupStack[i].Window;
9040             ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9041         }
9042         ImGui::TreePop();
9043     }
9044     if (ImGui::TreeNode("Internal state"))
9045     {
9046         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9047         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9048         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9049         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
9050         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]);
9051         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9052         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9053         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9054         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9055         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
9056         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9057         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9058         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9059         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9060         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9061         ImGui::TreePop();
9062     }
9063 
9064 
9065     if (g.IO.KeyCtrl && show_window_begin_order)
9066     {
9067         for (int n = 0; n < g.Windows.Size; n++)
9068         {
9069             ImGuiWindow* window = g.Windows[n];
9070             if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
9071                 continue;
9072             char buf[32];
9073             ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
9074             float font_size = ImGui::GetFontSize() * 2;
9075             ImDrawList* overlay_draw_list = GetOverlayDrawList(window);
9076             overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9077             overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf);
9078         }
9079     }
9080     ImGui::End();
9081 }
9082 
9083 //-----------------------------------------------------------------------------
9084 
9085 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9086 // Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
9087 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9088 #include "imgui_user.inl"
9089 #endif
9090 
9091 //-----------------------------------------------------------------------------
9092