1 // dear imgui, v1.68 WIP
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 
11 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
12 // See LICENSE.txt for copyright and licensing details (standard MIT License).
13 // This library is free but I need your support to sustain development and maintenance.
14 // Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README.
15 // Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui.
16 
17 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
18 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
19 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
20 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
21 // to a better solution or official support for them.
22 
23 /*
24 
25 Index of this file:
26 
27 DOCUMENTATION
28 
29 - MISSION STATEMENT
30 - END-USER GUIDE
31 - PROGRAMMER GUIDE (read me!)
32   - Read first.
33   - How to update to a newer version of Dear ImGui.
34   - Getting started with integrating Dear ImGui in your code/engine.
35   - This is how a simple application may look like (2 variations).
36   - This is how a simple rendering function may look like.
37   - Using gamepad/keyboard navigation controls.
38 - API BREAKING CHANGES (read me when you update!)
39 - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
40   - How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
41   - How can I display an image? What is ImTextureID, how does it works?
42   - How can I have multiple widgets with the same label or with an empty label? A primer on labels and the ID Stack.
43   - How can I use my own math types instead of ImVec2/ImVec4?
44   - How can I load a different font than the default?
45   - How can I easily use icons in my application?
46   - How can I load multiple fonts?
47   - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
48   - How can I interact with standard C++ types (such as std::string and std::vector)?
49   - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
50   - How can I use Dear ImGui on a platform that doesn't have a mouse or a keyboard? (input share, remoting, gamepad)
51   - I integrated Dear ImGui in my engine and the text or lines are blurry..
52   - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
53   - How can I help?
54 
55 CODE
56 (search for "[SECTION]" in the code to find them)
57 
58 // [SECTION] FORWARD DECLARATIONS
59 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
60 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
61 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
62 // [SECTION] MISC HELPER/UTILITIES (ImText* functions)
63 // [SECTION] MISC HELPER/UTILITIES (Color functions)
64 // [SECTION] ImGuiStorage
65 // [SECTION] ImGuiTextFilter
66 // [SECTION] ImGuiTextBuffer
67 // [SECTION] ImGuiListClipper
68 // [SECTION] RENDER HELPERS
69 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
70 // [SECTION] TOOLTIPS
71 // [SECTION] POPUPS
72 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
73 // [SECTION] COLUMNS
74 // [SECTION] DRAG AND DROP
75 // [SECTION] LOGGING/CAPTURING
76 // [SECTION] SETTINGS
77 // [SECTION] PLATFORM DEPENDENT HELPERS
78 // [SECTION] METRICS/DEBUG WINDOW
79 
80 */
81 
82 //-----------------------------------------------------------------------------
83 // DOCUMENTATION
84 //-----------------------------------------------------------------------------
85 
86 /*
87 
88  MISSION STATEMENT
89  =================
90 
91  - Easy to use to create code-driven and data-driven tools.
92  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
93  - Easy to hack and improve.
94  - Minimize screen real-estate usage.
95  - Minimize setup and maintenance.
96  - Minimize state storage on user side.
97  - Portable, minimize dependencies, run on target (consoles, phones, etc.).
98  - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
99    opening a tree node for the first time, etc. but a typical frame should not allocate anything).
100 
101  Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
102  - Doesn't look fancy, doesn't animate.
103  - Limited layout features, intricate layouts are typically crafted in code.
104 
105 
106  END-USER GUIDE
107  ==============
108 
109  - Double-click on title bar to collapse window.
110  - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
111  - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
112  - Click and drag on any empty space to move window.
113  - TAB/SHIFT+TAB to cycle through keyboard editable fields.
114  - CTRL+Click on a slider or drag box to input value as text.
115  - Use mouse wheel to scroll.
116  - Text editor:
117    - Hold SHIFT or use mouse to select text.
118    - CTRL+Left/Right to word jump.
119    - CTRL+Shift+Left/Right to select words.
120    - CTRL+A our Double-Click to select all.
121    - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
122    - CTRL+Z,CTRL+Y to undo/redo.
123    - ESCAPE to revert text to its original value.
124    - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
125    - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
126  - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
127  - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
128 
129 
130  PROGRAMMER GUIDE
131  ================
132 
133  READ FIRST:
134 
135  - Read the FAQ below this section!
136  - 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
137    or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
138  - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
139  - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
140  - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
141    You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
142  - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
143    For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI,
144    where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
145  - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
146  - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
147  - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
148    If you get an assert, read the messages and comments around the assert.
149  - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace.
150  - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
151    See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
152    However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
153  - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
154 
155  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI:
156 
157  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
158  - Or maintain your own branch where you have imconfig.h modified.
159  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
160    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
161    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
162    likely be a comment about it. Please report any issue to the GitHub page!
163  - Try to keep your copy of dear imgui reasonably up to date.
164 
165  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE:
166 
167  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
168  - Add the Dear ImGui source files to your projects or using your preferred build system.
169    It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
170  - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating imgui types with your own maths types.
171  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
172  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
173    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
174    phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
175  - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
176  - 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.
177 
178  HOW A SIMPLE APPLICATION MAY LOOK LIKE:
179  EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
180 
181      // Application init: create a dear imgui context, setup some options, load fonts
182      ImGui::CreateContext();
183      ImGuiIO& io = ImGui::GetIO();
184      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
185      // TODO: Fill optional fields of the io structure later.
186      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
187 
188      // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
189      ImGui_ImplWin32_Init(hwnd);
190      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
191 
192      // Application main loop
193      while (true)
194      {
195          // Feed inputs to dear imgui, start new frame
196          ImGui_ImplDX11_NewFrame();
197          ImGui_ImplWin32_NewFrame();
198          ImGui::NewFrame();
199 
200          // Any application code here
201          ImGui::Text("Hello, world!");
202 
203          // Render dear imgui into screen
204          ImGui::Render();
205          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
206          g_pSwapChain->Present(1, 0);
207      }
208 
209      // Shutdown
210      ImGui_ImplDX11_Shutdown();
211      ImGui_ImplWin32_Shutdown();
212      ImGui::DestroyContext();
213 
214  HOW A SIMPLE APPLICATION MAY LOOK LIKE:
215  EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE.
216 
217      // Application init: create a dear imgui context, setup some options, load fonts
218      ImGui::CreateContext();
219      ImGuiIO& io = ImGui::GetIO();
220      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
221      // TODO: Fill optional fields of the io structure later.
222      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
223 
224      // Build and load the texture atlas into a texture
225      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
226      int width, height;
227      unsigned char* pixels = NULL;
228      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
229 
230      // At this point you've got the texture data and you need to upload that your your graphic system:
231      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
232      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
233      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
234      io.Fonts->TexID = (void*)texture;
235 
236      // Application main loop
237      while (true)
238      {
239         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
240         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
241         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
242         io.DisplaySize.x = 1920.0f;             // set the current display width
243         io.DisplaySize.y = 1280.0f;             // set the current display height here
244         io.MousePos = my_mouse_pos;             // set the mouse position
245         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
246         io.MouseDown[1] = my_mouse_buttons[1];
247 
248         // Call NewFrame(), after this point you can use ImGui::* functions anytime
249         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
250         ImGui::NewFrame();
251 
252         // Most of your application code here
253         ImGui::Text("Hello, world!");
254         MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
255         MyGameRender(); // may use any ImGui functions as well!
256 
257         // Render imgui, swap buffers
258         // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
259         ImGui::EndFrame();
260         ImGui::Render();
261         ImDrawData* draw_data = ImGui::GetDrawData();
262         MyImGuiRenderFunction(draw_data);
263         SwapBuffers();
264      }
265 
266      // Shutdown
267      ImGui::DestroyContext();
268 
269  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE:
270 
271     void void MyImGuiRenderFunction(ImDrawData* draw_data)
272     {
273        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
274        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
275        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
276        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
277        for (int n = 0; n < draw_data->CmdListsCount; n++)
278        {
279           const ImDrawList* cmd_list = draw_data->CmdLists[n];
280           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by ImGui
281           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by ImGui
282           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
283           {
284              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
285              if (pcmd->UserCallback)
286              {
287                  pcmd->UserCallback(cmd_list, pcmd);
288              }
289              else
290              {
291                  // The texture for the draw call is specified by pcmd->TextureId.
292                  // The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization.
293                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
294 
295                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
296                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
297                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
298                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
299                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
300                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
301                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
302                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
303                  ImVec2 pos = draw_data->DisplayPos;
304                  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));
305 
306                  // Render 'pcmd->ElemCount/3' indexed triangles.
307                  // 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.
308                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
309              }
310              idx_buffer += pcmd->ElemCount;
311           }
312        }
313     }
314 
315  - The examples/ folders contains many actual implementation of the pseudo-codes above.
316  - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
317    They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs
318    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.
319  - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues!
320 
321  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
322 
323  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
324  - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
325  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
326  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
327  - Gamepad:
328     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
329     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
330       Note that io.NavInputs[] is cleared by EndFrame().
331     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
332          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
333     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
334       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.).
335     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
336     - 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
337       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
338  - Keyboard:
339     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
340       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
341     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
342       will be set. For more advanced uses, you may want to read from:
343        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
344        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
345        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
346       Please reach out if you think the game vs navigation input sharing could be improved.
347  - Mouse:
348     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
349     - 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.
350     - 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.
351       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
352       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.
353       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.
354       (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!)
355       (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
356        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
357 
358 
359  API BREAKING CHANGES
360  ====================
361 
362  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
363  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.
364  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.
365  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
366 
367  - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value!
368  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
369  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
370  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Keep redirection typedef (will obsolete).
371  - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects.
372  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
373  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
374  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
375  - 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.
376                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
377  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
378  - 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.
379                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
380                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
381  - 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).
382  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
383  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
384  - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature.
385  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
386  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
387  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
388  - 2018/06/08 (1.62) - examples: the imgui_impl_xxx files have been split to separate platform (Win32, Glfw, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan,  etc.).
389                        old binding will still work as is, however prefer using the separated bindings as they will be updated to be multi-viewport conformant.
390                        when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
391  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
392  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
393  - 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.
394                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
395                        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.
396                        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.
397  - 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",
398                        consistent with other functions. Kept redirection functions (will obsolete).
399  - 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.
400  - 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).
401  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
402  - 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.
403  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
404  - 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.
405  - 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.
406  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
407                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
408                        - removed Shutdown() function, as DestroyContext() serve this purpose.
409                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
410                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
411                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
412  - 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.
413  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
414  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
415  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
416  - 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.
417  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
418  - 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
419  - 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.
420  - 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.
421  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
422  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
423                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
424  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
425  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
426  - 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.
427  - 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.
428                        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.
429  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
430  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
431  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
432  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
433  - 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.
434  - 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.
435  - 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.
436                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
437  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
438  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
439  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Keep redirection typedef (will obsolete).
440  - 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).
441  - 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)".
442  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
443                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
444                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
445  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
446  - 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.
447  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
448  - 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.
449  - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
450  - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
451  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
452  - 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.
453                      - 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.
454                      - 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))'
455  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
456  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
457  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
458  - 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().
459  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
460  - 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.
461  - 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.
462  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
463                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
464                        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.
465                        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.
466                            ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
467                            {
468                                float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
469                                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);
470                            }
471                        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.
472  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
473  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
474  - 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).
475  - 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.
476  - 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).
477  - 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)
478  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
479  - 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.
480  - 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.
481  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
482  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
483  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
484                        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.
485                        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!
486  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
487  - 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.
488  - 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
489  - 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.
490                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
491  - 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.
492                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
493                      - 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.
494                      - the signature of the io.RenderDrawListsFn handler has changed!
495                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
496                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
497                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
498                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
499                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
500                      - 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.
501                      - 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!
502                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
503  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
504  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
505  - 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.
506  - 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
507  - 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!
508  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
509  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
510  - 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.
511  - 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.
512  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
513  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
514  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
515  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
516  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
517  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
518  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
519  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
520  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
521  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
522  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
523  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
524  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
525  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
526  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
527  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
528  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
529               (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
530                        font init:  { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; }
531                        became:     { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; }
532                        you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
533                        it is now recommended that you sample the font texture with bilinear interpolation.
534               (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
535               (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
536               (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
537  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
538  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
539  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
540  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
541  - 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)
542  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
543  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
544  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
545  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
546  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
547  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
548 
549 
550  FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
551  ======================================
552 
553  Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
554  A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
555     - 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.
556     - 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.
557     - 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).
558     Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
559      This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
560     Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
561      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.
562      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
563      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().
564     Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
565      have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
566      were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
567 
568  Q: How can I display an image? What is ImTextureID, how does it works?
569  A: Short explanation:
570     - 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.
571     - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value.
572     - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
573       Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
574 
575     Long explanation:
576     - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
577       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
578       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.).
579     - 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.
580       We carry the information to identify a "texture" in the ImTextureID type.
581       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.
582       Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
583     - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
584       an image from the end-user perspective. This is what the _examples_ rendering functions are using:
585 
586          OpenGL:     ImTextureID = GLuint                       (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
587          DirectX9:   ImTextureID = LPDIRECT3DTEXTURE9           (see ImGui_ImplDX9_RenderDrawData()     function in imgui_impl_dx9.cpp)
588          DirectX11:  ImTextureID = ID3D11ShaderResourceView*    (see ImGui_ImplDX11_RenderDrawData()    function in imgui_impl_dx11.cpp)
589          DirectX12:  ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE  (see ImGui_ImplDX12_RenderDrawData()    function in imgui_impl_dx12.cpp)
590 
591       For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
592       Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
593       tying together both the texture and information about its format and how to read it.
594     - 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
595       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
596       is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
597       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
598       representation suggested by the example bindings is probably the best choice.
599       (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
600 
601     User code may do:
602 
603         // Cast our texture type to ImTextureID / void*
604         MyTexture* texture = g_CoffeeTableTexture;
605         ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
606 
607     The renderer function called after ImGui::Render() will receive that same value that the user code passed:
608 
609         // Cast ImTextureID / void* stored in the draw command as our texture type
610         MyTexture* texture = (MyTexture*)pcmd->TextureId;
611         MyEngineBindTexture2D(texture);
612 
613     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.
614     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.
615     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.
616 
617     Here's a simplified OpenGL example using stb_image.h:
618 
619         // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
620         #define STB_IMAGE_IMPLEMENTATION
621         #include <stb_image.h>
622         [...]
623         int my_image_width, my_image_height;
624         unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
625 
626         // Turn the RGBA pixel data into an OpenGL texture:
627         GLuint my_opengl_texture;
628         glGenTextures(1, &my_opengl_texture);
629         glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
630         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
631         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
632         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
633         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
634 
635         // 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:
636         ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
637 
638     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.
639     Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
640     Examples:
641 
642         GLuint my_tex = XXX;
643         void* my_void_ptr;
644         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)
645         my_tex = (GLuint)(intptr_t)my_void_ptr;                 // cast a void* into a GLuint
646 
647         ID3D11ShaderResourceView* my_dx11_srv = XXX;
648         void* my_void_ptr;
649         my_void_ptr = (void*)my_dx11_srv;                       // cast a ID3D11ShaderResourceView* into an opaque void*
650         my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr;   // cast a void* into a ID3D11ShaderResourceView*
651 
652     Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
653 
654  Q: How can I have multiple widgets with the same label or with an empty label?
655  Q: I have multiple widgets with the same label, and only the first one works. Why is that?
656  A: A primer on labels and the ID Stack...
657 
658     Dear ImGui internally need to uniquely identify UI elements.
659     Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
660     Interactive widgets (such as calls to Button buttons) need a unique ID.
661     Unique ID are used internally to track active widgets and occasionally associate state to widgets.
662     Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
663 
664    - Unique ID are often derived from a string label:
665 
666        Button("OK");          // Label = "OK",     ID = hash of (..., "OK")
667        Button("Cancel");      // Label = "Cancel", ID = hash of (..., "Cancel")
668 
669    - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
670      two buttons labeled "OK" in different windows or different tree locations is fine.
671      We used "..." above to signify whatever was already pushed to the ID stack previously:
672 
673        Begin("MyWindow");
674        Button("OK");          // Label = "OK",     ID = hash of ("MyWindow", "OK")
675        End();
676        Begin("MyOtherWindow");
677        Button("OK");          // Label = "OK",     ID = hash of ("MyOtherWindow", "OK")
678        End();
679 
680    - If you have a same ID twice in the same location, you'll have a conflict:
681 
682        Button("OK");
683        Button("OK");          // ID collision! Interacting with either button will trigger the first one.
684 
685      Fear not! this is easy to solve and there are many ways to solve it!
686 
687    - Solving ID conflict in a simple/local context:
688      When passing a label you can optionally specify extra ID information within string itself.
689      Use "##" to pass a complement to the ID that won't be visible to the end-user.
690      This helps solving the simple collision cases when you know e.g. at compilation time which items
691      are going to be created:
692 
693        Begin("MyWindow");
694        Button("Play");        // Label = "Play",   ID = hash of ("MyWindow", "Play")
695        Button("Play##foo1");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo1")  // Different from above
696        Button("Play##foo2");  // Label = "Play",   ID = hash of ("MyWindow", "Play##foo2")  // Different from above
697        End();
698 
699    - If you want to completely hide the label, but still need an ID:
700 
701        Checkbox("##On", &b);  // Label = "",       ID = hash of (..., "##On")   // No visible label, just a checkbox!
702 
703    - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
704      you to animate labels. For example you may want to include varying information in a window title bar,
705      but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
706 
707        Button("Hello###ID");  // Label = "Hello",  ID = hash of (..., "###ID")
708        Button("World###ID");  // Label = "World",  ID = hash of (..., "###ID")  // Same as above, even though the label looks different
709 
710        sprintf(buf, "My game (%f FPS)###MyGame", fps);
711        Begin(buf);            // Variable title,   ID = hash of "MyGame"
712 
713    - Solving ID conflict in a more general manner:
714      Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
715      within the same window. This is the most convenient way of distinguishing ID when iterating and
716      creating many UI elements programmatically.
717      You can push a pointer, a string or an integer value into the ID stack.
718      Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
719      At each level of the stack we store the seed used for items at this level of the ID stack.
720 
721      Begin("Window");
722        for (int i = 0; i < 100; i++)
723        {
724          PushID(i);           // Push i to the id tack
725          Button("Click");     // Label = "Click",  ID = hash of ("Window", i, "Click")
726          PopID();
727        }
728        for (int i = 0; i < 100; i++)
729        {
730          MyObject* obj = Objects[i];
731          PushID(obj);
732          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj pointer, "Click")
733          PopID();
734        }
735        for (int i = 0; i < 100; i++)
736        {
737          MyObject* obj = Objects[i];
738          PushID(obj->Name);
739          Button("Click");     // Label = "Click",  ID = hash of ("Window", obj->Name, "Click")
740          PopID();
741        }
742        End();
743 
744    - You can stack multiple prefixes into the ID stack:
745 
746        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
747        PushID("node");
748        Button("Click");       // Label = "Click",  ID = hash of (..., "node", "Click")
749          PushID(my_ptr);
750            Button("Click");   // Label = "Click",  ID = hash of (..., "node", my_ptr, "Click")
751          PopID();
752        PopID();
753 
754    - Tree nodes implicitly creates a scope for you by calling PushID().
755 
756        Button("Click");       // Label = "Click",  ID = hash of (..., "Click")
757        if (TreeNode("node"))  // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
758        {
759          Button("Click");     // Label = "Click",  ID = hash of (..., "node", "Click")
760          TreePop();
761        }
762 
763    - When working with trees, ID are used to preserve the open/close state of each tree node.
764      Depending on your use cases you may want to use strings, indices or pointers as ID.
765       e.g. when following a single pointer that may change over time, using a static string as ID
766        will preserve your node open/closed state when the targeted object change.
767       e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
768        node open/closed state differently. See what makes more sense in your situation!
769 
770  Q: How can I use my own math types instead of ImVec2/ImVec4?
771  A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
772     This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
773 
774  Q: How can I load a different font than the default?
775  A: Use the font atlas to load the TTF/OTF file you want:
776       ImGuiIO& io = ImGui::GetIO();
777       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
778       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
779     Default is ProggyClean.ttf, monospace, rendered at size 13, embedded in dear imgui's source code.
780     (Tip: monospace fonts are convenient because they allow to facilitate horizontal alignment directly at the string level.)
781     (Read the 'misc/fonts/README.txt' file for more details about font loading.)
782 
783     New programmers: remember that in C/C++ and most programming languages if you want to use a
784     backslash \ within a string literal, you need to write it double backslash "\\":
785       io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels);   // WRONG (you are escape the M here!)
786       io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels);  // CORRECT
787       io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels);   // ALSO CORRECT
788 
789  Q: How can I easily use icons in my application?
790  A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
791     main font. Then you can refer to icons within your strings.
792     You may want to see ImFontConfig::GlyphMinAdvanceX to make your icon look monospace to facilitate alignment.
793     (Read the 'misc/fonts/README.txt' file for more details about icons font loading.)
794 
795  Q: How can I load multiple fonts?
796  A: Use the font atlas to pack them into a single texture:
797     (Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.)
798 
799       ImGuiIO& io = ImGui::GetIO();
800       ImFont* font0 = io.Fonts->AddFontDefault();
801       ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
802       ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
803       io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
804       // the first loaded font gets used by default
805       // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
806 
807       // Options
808       ImFontConfig config;
809       config.OversampleH = 2;
810       config.OversampleV = 1;
811       config.GlyphOffset.y -= 1.0f;      // Move everything by 1 pixels up
812       config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
813       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config);
814 
815       // Combine multiple fonts into one (e.g. for icon fonts)
816       static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
817       ImFontConfig config;
818       config.MergeMode = true;
819       io.Fonts->AddFontDefault();
820       io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
821       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
822 
823  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
824  A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
825 
826       // Add default Japanese ranges
827       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
828 
829       // 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)
830       ImVector<ImWchar> ranges;
831       ImFontGlyphRangesBuilder builder;
832       builder.AddText("Hello world");                        // Add a string (here "Hello world" contains 7 unique characters)
833       builder.AddChar(0x7262);                               // Add a specific character
834       builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
835       builder.BuildRanges(&ranges);                          // Build the final result (ordered ranges with all the unique characters submitted)
836       io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
837 
838     All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
839     by using the u8"hello" syntax. Specifying literal in your source code using a local code page
840     (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
841     Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
842 
843     Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
844     The applications in examples/ are doing that.
845     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).
846     You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
847     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
848     the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
849 
850  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
851  A: - Being highly portable (bindings for several languages, frameworks, programming style, obscure or older platforms/compilers),
852       and aiming for compatibility & performance suitable for every modern real-time game engines, dear imgui does not use
853       any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases.
854     - To use ImGui::InputText() with a std::string or any resizable string class, see misc/cpp/imgui_stdlib.h.
855     - To use combo boxes and list boxes with std::vector or any other data structure: the BeginCombo()/EndCombo() API
856       lets you iterate and submit items yourself, so does the ListBoxHeader()/ListBoxFooter() API.
857       Prefer using them over the old and awkward Combo()/ListBox() api.
858     - Generally for most high-level types you should be able to access the underlying data type.
859       You may write your own one-liner wrappers to facilitate user code (tip: add new functions in ImGui:: namespace from your code).
860     - Dear ImGui applications often need to make intensive use of strings. It is expected that many of the strings you will pass
861       to the API are raw literals (free in C/C++) or allocated in a manner that won't incur a large cost on your application.
862       Please bear in mind that using std::string on applications with large amount of UI may incur unsatisfactory performances.
863       Modern implementations of std::string often include small-string optimization (which is often a local buffer) but those
864       are not configurable and not the same across implementations.
865     - If you are finding your UI traversal cost to be too large, make sure your string usage is not leading to excessive amount
866       of heap allocations. Consider using literals, statically sized buffers and your own helper functions. A common pattern
867       is that you will need to build lots of strings on the fly, and their maximum length can be easily be scoped ahead.
868       One possible implementation of a helper to facilitate printf-style building of strings: https://github.com/ocornut/Str
869       This is a small helper where you can instance strings with configurable local buffers length. Many game engines will
870       provide similar or better string helpers.
871 
872  Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
873  A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags.
874       (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse)
875       Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
876     - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
877     - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create
878       your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data.
879 
880  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
881  A: - You can control Dear ImGui with a gamepad. Read about navigation in "Using gamepad/keyboard navigation controls".
882       (short version: map gamepad inputs into the io.NavInputs[] array + set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad)
883     - You can share your computer mouse seamlessly with your console/tablet/phone using Synergy (https://symless.com/synergy)
884       This is the preferred solution for developer productivity.
885       In particular, the "micro-synergy-client" repository (https://github.com/symless/micro-synergy-client) has simple
886       and portable source code (uSynergy.c/.h) for a small embeddable client that you can use on any platform to connect
887       to your host computer, based on the Synergy 1.x protocol. Make sure you download the Synergy 1 server on your computer.
888       Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-like protocols.
889     - You may also use a third party solution such as Remote ImGui (https://github.com/JordiRos/remoteimgui) which sends
890       the vertices to render over the local network, allowing you to use Dear ImGui even on a screen-less machine.
891     - For touch inputs, you can increase the hit box of widgets (via the style.TouchPadding setting) to accommodate
892       for the lack of precision of touch inputs, but it is recommended you use a mouse or gamepad to allow optimizing
893       for screen real-estate and precision.
894 
895  Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
896  A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
897     Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
898 
899  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
900  A: You are probably mishandling the clipping rectangles in your render function.
901     Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
902 
903  Q: How can I help?
904  A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
905       and see how you want to help and can help!
906     - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui.
907     - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README.
908     - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
909       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1902). Visuals are ideal as they inspire other programmers.
910       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
911     - 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).
912 
913  - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
914         this is also useful to set yourself in the context of another window (to get/set other settings)
915  - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
916  - 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
917         of a deep nested inner loop in your code.
918  - tip: you can call Render() multiple times (e.g for VR renders).
919  - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
920 
921 */
922 
923 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
924 #define _CRT_SECURE_NO_WARNINGS
925 #endif
926 
927 #include "imgui.h"
928 #ifndef IMGUI_DEFINE_MATH_OPERATORS
929 #define IMGUI_DEFINE_MATH_OPERATORS
930 #endif
931 #include "imgui_internal.h"
932 
933 #include <ctype.h>      // toupper, isprint
934 #include <stdio.h>      // vsnprintf, sscanf, printf
935 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
936 #include <stddef.h>     // intptr_t
937 #else
938 #include <stdint.h>     // intptr_t
939 #endif
940 
941 // Debug options
942 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
943 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
944 
945 // Visual Studio warnings
946 #ifdef _MSC_VER
947 #pragma warning (disable: 4127)     // condition expression is constant
948 #pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
949 #endif
950 
951 // Clang/GCC warnings with -Weverything
952 #ifdef __clang__
953 #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!
954 #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
955 #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.
956 #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.
957 #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.
958 #pragma clang diagnostic ignored "-Wglobal-constructors"    // warning : declaration requires a global destructor           // similar to above, not sure what the exact difference it.
959 #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
960 #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.
961 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning : cast to 'void *' from smaller integer type 'int'
962 #if __has_warning("-Wzero-as-null-pointer-constant")
963 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning : zero as null pointer constant              // some standard header variations use #define NULL 0
964 #endif
965 #if __has_warning("-Wdouble-promotion")
966 #pragma clang diagnostic ignored "-Wdouble-promotion"       // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
967 #endif
968 #elif defined(__GNUC__)
969 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
970 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
971 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
972 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
973 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
974 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
975 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
976 #if __GNUC__ >= 8
977 #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
978 #endif
979 #endif
980 
981 // 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.
982 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
983 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
984 
985 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
986 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
987 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
988 
989 //-------------------------------------------------------------------------
990 // [SECTION] FORWARD DECLARATIONS
991 //-------------------------------------------------------------------------
992 
993 static void             SetCurrentWindow(ImGuiWindow* window);
994 static void             FindHoveredWindow();
995 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
996 static void             CheckStacksSize(ImGuiWindow* window, bool write);
997 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
998 
999 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
1000 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1001 
1002 static ImRect           GetViewportRect();
1003 
1004 // Settings
1005 static void*            SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1006 static void             SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1007 static void             SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
1008 
1009 // Platform Dependents default implementation for IO functions
1010 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
1011 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
1012 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
1013 
1014 namespace ImGui
1015 {
1016 static bool             BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
1017 
1018 // Navigation
1019 static void             NavUpdate();
1020 static void             NavUpdateWindowing();
1021 static void             NavUpdateWindowingList();
1022 static void             NavUpdateMoveResult();
1023 static float            NavUpdatePageUpPageDown(int allowed_dir_flags);
1024 static inline void      NavUpdateAnyRequestFlag();
1025 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
1026 static ImVec2           NavCalcPreferredRefPos();
1027 static void             NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
1028 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
1029 
1030 // Misc
1031 static void             UpdateMouseInputs();
1032 static void             UpdateMouseWheel();
1033 static void             UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
1034 static void             RenderOuterBorders(ImGuiWindow* window);
1035 
1036 }
1037 
1038 //-----------------------------------------------------------------------------
1039 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
1040 //-----------------------------------------------------------------------------
1041 
1042 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1043 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
1044 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
1045 //    SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
1046 //    In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
1047 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
1048 //    If you want thread-safety to allow N threads to access N different contexts, you can:
1049 //    - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
1050 //          struct ImGuiContext;
1051 //          extern thread_local ImGuiContext* MyImGuiTLS;
1052 //          #define GImGui MyImGuiTLS
1053 //      And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
1054 //    - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1055 //    - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
1056 #ifndef GImGui
1057 ImGuiContext*   GImGui = NULL;
1058 #endif
1059 
1060 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1061 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
1062 // 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.
1063 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)1064 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)1065 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
1066 #else
MallocWrapper(size_t size,void * user_data)1067 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)1068 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1069 #endif
1070 
1071 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
1072 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
1073 static void*    GImAllocatorUserData = NULL;
1074 
1075 //-----------------------------------------------------------------------------
1076 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
1077 //-----------------------------------------------------------------------------
1078 
ImGuiStyle()1079 ImGuiStyle::ImGuiStyle()
1080 {
1081     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
1082     WindowPadding           = ImVec2(8,8);      // Padding within a window
1083     WindowRounding          = 7.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows
1084     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1085     WindowMinSize           = ImVec2(32,32);    // Minimum window size
1086     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
1087     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1088     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1089     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1090     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1091     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
1092     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1093     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1094     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
1095     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1096     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!
1097     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1098     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
1099     ScrollbarSize           = 16.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
1100     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
1101     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
1102     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1103     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1104     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
1105     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1106     SelectableTextAlign     = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
1107     DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
1108     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.
1109     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1110     AntiAliasedLines        = true;             // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1111     AntiAliasedFill         = true;             // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1112     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.
1113 
1114     // Default theme
1115     ImGui::StyleColorsDark(this);
1116 }
1117 
1118 // 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.
1119 // 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)1120 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1121 {
1122     WindowPadding = ImFloor(WindowPadding * scale_factor);
1123     WindowRounding = ImFloor(WindowRounding * scale_factor);
1124     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1125     ChildRounding = ImFloor(ChildRounding * scale_factor);
1126     PopupRounding = ImFloor(PopupRounding * scale_factor);
1127     FramePadding = ImFloor(FramePadding * scale_factor);
1128     FrameRounding = ImFloor(FrameRounding * scale_factor);
1129     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1130     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1131     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1132     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1133     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1134     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1135     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1136     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1137     GrabRounding = ImFloor(GrabRounding * scale_factor);
1138     TabRounding = ImFloor(TabRounding * scale_factor);
1139     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1140     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1141     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1142 }
1143 
ImGuiIO()1144 ImGuiIO::ImGuiIO()
1145 {
1146     // Most fields are initialized with zero
1147     memset(this, 0, sizeof(*this));
1148 
1149     // Settings
1150     ConfigFlags = ImGuiConfigFlags_None;
1151     BackendFlags = ImGuiBackendFlags_None;
1152     DisplaySize = ImVec2(-1.0f, -1.0f);
1153     DeltaTime = 1.0f/60.0f;
1154     IniSavingRate = 5.0f;
1155     IniFilename = "imgui.ini";
1156     LogFilename = "imgui_log.txt";
1157     MouseDoubleClickTime = 0.30f;
1158     MouseDoubleClickMaxDist = 6.0f;
1159     for (int i = 0; i < ImGuiKey_COUNT; i++)
1160         KeyMap[i] = -1;
1161     KeyRepeatDelay = 0.250f;
1162     KeyRepeatRate = 0.050f;
1163     UserData = NULL;
1164 
1165     Fonts = NULL;
1166     FontGlobalScale = 1.0f;
1167     FontDefault = NULL;
1168     FontAllowUserScaling = false;
1169     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1170 
1171     // Miscellaneous options
1172     MouseDrawCursor = false;
1173 #ifdef __APPLE__
1174     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1175 #else
1176     ConfigMacOSXBehaviors = false;
1177 #endif
1178     ConfigInputTextCursorBlink = true;
1179     ConfigWindowsResizeFromEdges = true;
1180     ConfigWindowsMoveFromTitleBarOnly = false;
1181 
1182     // Platform Functions
1183     BackendPlatformName = BackendRendererName = NULL;
1184     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1185     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1186     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1187     ClipboardUserData = NULL;
1188     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1189     ImeWindowHandle = NULL;
1190 
1191 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1192     RenderDrawListsFn = NULL;
1193 #endif
1194 
1195     // Input (NB: we already have memset zero the entire structure!)
1196     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1197     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1198     MouseDragThreshold = 6.0f;
1199     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1200     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1201     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1202 }
1203 
1204 // Pass in translated ASCII characters for text input.
1205 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1206 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)1207 void ImGuiIO::AddInputCharacter(ImWchar c)
1208 {
1209     InputQueueCharacters.push_back(c);
1210 }
1211 
AddInputCharactersUTF8(const char * utf8_chars)1212 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1213 {
1214     while (*utf8_chars != 0)
1215     {
1216         unsigned int c = 0;
1217         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1218         if (c > 0 && c <= 0xFFFF)
1219             InputQueueCharacters.push_back((ImWchar)c);
1220     }
1221 }
1222 
ClearInputCharacters()1223 void ImGuiIO::ClearInputCharacters()
1224 {
1225     InputQueueCharacters.resize(0);
1226 }
1227 
1228 //-----------------------------------------------------------------------------
1229 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1230 //-----------------------------------------------------------------------------
1231 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1232 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1233 {
1234     ImVec2 ap = p - a;
1235     ImVec2 ab_dir = b - a;
1236     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1237     if (dot < 0.0f)
1238         return a;
1239     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1240     if (dot > ab_len_sqr)
1241         return b;
1242     return a + ab_dir * dot / ab_len_sqr;
1243 }
1244 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1245 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1246 {
1247     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1248     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1249     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1250     return ((b1 == b2) && (b2 == b3));
1251 }
1252 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1253 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1254 {
1255     ImVec2 v0 = b - a;
1256     ImVec2 v1 = c - a;
1257     ImVec2 v2 = p - a;
1258     const float denom = v0.x * v1.y - v1.x * v0.y;
1259     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1260     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1261     out_u = 1.0f - out_v - out_w;
1262 }
1263 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1264 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1265 {
1266     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1267     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1268     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1269     float dist2_ab = ImLengthSqr(p - proj_ab);
1270     float dist2_bc = ImLengthSqr(p - proj_bc);
1271     float dist2_ca = ImLengthSqr(p - proj_ca);
1272     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1273     if (m == dist2_ab)
1274         return proj_ab;
1275     if (m == dist2_bc)
1276         return proj_bc;
1277     return proj_ca;
1278 }
1279 
1280 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
ImStricmp(const char * str1,const char * str2)1281 int ImStricmp(const char* str1, const char* str2)
1282 {
1283     int d;
1284     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1285     return d;
1286 }
1287 
ImStrnicmp(const char * str1,const char * str2,size_t count)1288 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1289 {
1290     int d = 0;
1291     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1292     return d;
1293 }
1294 
ImStrncpy(char * dst,const char * src,size_t count)1295 void ImStrncpy(char* dst, const char* src, size_t count)
1296 {
1297     if (count < 1)
1298         return;
1299     if (count > 1)
1300         strncpy(dst, src, count - 1);
1301     dst[count - 1] = 0;
1302 }
1303 
ImStrdup(const char * str)1304 char* ImStrdup(const char* str)
1305 {
1306     size_t len = strlen(str);
1307     void* buf = ImGui::MemAlloc(len + 1);
1308     return (char*)memcpy(buf, (const void*)str, len + 1);
1309 }
1310 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1311 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1312 {
1313     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1314     size_t src_size = strlen(src) + 1;
1315     if (dst_buf_size < src_size)
1316     {
1317         ImGui::MemFree(dst);
1318         dst = (char*)ImGui::MemAlloc(src_size);
1319         if (p_dst_size)
1320             *p_dst_size = src_size;
1321     }
1322     return (char*)memcpy(dst, (const void*)src, src_size);
1323 }
1324 
ImStrchrRange(const char * str,const char * str_end,char c)1325 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1326 {
1327     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1328     return p;
1329 }
1330 
ImStrlenW(const ImWchar * str)1331 int ImStrlenW(const ImWchar* str)
1332 {
1333     //return (int)wcslen((const wchar_t*)str);	// FIXME-OPT: Could use this when wchar_t are 16-bits
1334     int n = 0;
1335     while (*str++) n++;
1336     return n;
1337 }
1338 
1339 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1340 const char* ImStreolRange(const char* str, const char* str_end)
1341 {
1342     const char* p = (const char*)memchr(str, '\n', str_end - str);
1343     return p ? p : str_end;
1344 }
1345 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1346 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1347 {
1348     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1349         buf_mid_line--;
1350     return buf_mid_line;
1351 }
1352 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1353 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1354 {
1355     if (!needle_end)
1356         needle_end = needle + strlen(needle);
1357 
1358     const char un0 = (char)toupper(*needle);
1359     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1360     {
1361         if (toupper(*haystack) == un0)
1362         {
1363             const char* b = needle + 1;
1364             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1365                 if (toupper(*a) != toupper(*b))
1366                     break;
1367             if (b == needle_end)
1368                 return haystack;
1369         }
1370         haystack++;
1371     }
1372     return NULL;
1373 }
1374 
1375 // 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)1376 void ImStrTrimBlanks(char* buf)
1377 {
1378     char* p = buf;
1379     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1380         p++;
1381     char* p_start = p;
1382     while (*p != 0)                         // Find end of string
1383         p++;
1384     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1385         p--;
1386     if (p_start != buf)                     // Copy memory if we had leading blanks
1387         memmove(buf, p_start, p - p_start);
1388     buf[p - p_start] = 0;                   // Zero terminate
1389 }
1390 
1391 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1392 // 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.
1393 // B) When buf==NULL vsnprintf() will return the output size.
1394 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1395 
1396 //#define IMGUI_USE_STB_SPRINTF
1397 #ifdef IMGUI_USE_STB_SPRINTF
1398 #define STB_SPRINTF_IMPLEMENTATION
1399 #include "imstb_sprintf.h"
1400 #endif
1401 
1402 #if defined(_MSC_VER) && !defined(vsnprintf)
1403 #define vsnprintf _vsnprintf
1404 #endif
1405 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1406 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1407 {
1408     va_list args;
1409     va_start(args, fmt);
1410 #ifdef IMGUI_USE_STB_SPRINTF
1411     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1412 #else
1413     int w = vsnprintf(buf, buf_size, fmt, args);
1414 #endif
1415     va_end(args);
1416     if (buf == NULL)
1417         return w;
1418     if (w == -1 || w >= (int)buf_size)
1419         w = (int)buf_size - 1;
1420     buf[w] = 0;
1421     return w;
1422 }
1423 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1424 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1425 {
1426 #ifdef IMGUI_USE_STB_SPRINTF
1427     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1428 #else
1429     int w = vsnprintf(buf, buf_size, fmt, args);
1430 #endif
1431     if (buf == NULL)
1432         return w;
1433     if (w == -1 || w >= (int)buf_size)
1434         w = (int)buf_size - 1;
1435     buf[w] = 0;
1436     return w;
1437 }
1438 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1439 
1440 // CRC32 needs a 1KB lookup table (not cache friendly)
1441 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1442 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1443 static const ImU32 GCrc32LookupTable[256] =
1444 {
1445     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1446     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1447     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1448     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1449     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1450     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1451     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1452     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1453     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1454     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1455     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1456     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1457     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1458     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1459     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1460     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1461 };
1462 
1463 // Known size hash
1464 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1465 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashData(const void * data_p,size_t data_size,ImU32 seed)1466 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1467 {
1468     ImU32 crc = ~seed;
1469     const unsigned char* data = (const unsigned char*)data_p;
1470     const ImU32* crc32_lut = GCrc32LookupTable;
1471     while (data_size-- != 0)
1472         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1473     return ~crc;
1474 }
1475 
1476 // Zero-terminated string hash, with support for ### to reset back to seed value
1477 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1478 // Because this syntax is rarely used we are optimizing for the common case.
1479 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1480 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1481 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashStr(const char * data,size_t data_size,ImU32 seed)1482 ImU32 ImHashStr(const char* data, size_t data_size, ImU32 seed)
1483 {
1484     seed = ~seed;
1485     ImU32 crc = seed;
1486     const unsigned char* src = (const unsigned char*)data;
1487     const ImU32* crc32_lut = GCrc32LookupTable;
1488     if (data_size != 0)
1489     {
1490         while (data_size-- != 0)
1491         {
1492             unsigned char c = *src++;
1493             if (c == '#' && src[0] == '#' && src[1] == '#')
1494                 crc = seed;
1495             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1496         }
1497     }
1498     else
1499     {
1500         while (unsigned char c = *src++)
1501         {
1502             if (c == '#' && src[0] == '#' && src[1] == '#')
1503                 crc = seed;
1504             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1505         }
1506     }
1507     return ~crc;
1508 }
1509 
ImFileOpen(const char * filename,const char * mode)1510 FILE* ImFileOpen(const char* filename, const char* mode)
1511 {
1512 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1513     // 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)
1514     const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1515     const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1516     ImVector<ImWchar> buf;
1517     buf.resize(filename_wsize + mode_wsize);
1518     ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1519     ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1520     return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1521 #else
1522     return fopen(filename, mode);
1523 #endif
1524 }
1525 
1526 // Load file content into memory
1527 // 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)1528 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1529 {
1530     IM_ASSERT(filename && file_open_mode);
1531     if (out_file_size)
1532         *out_file_size = 0;
1533 
1534     FILE* f;
1535     if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1536         return NULL;
1537 
1538     long file_size_signed;
1539     if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1540     {
1541         fclose(f);
1542         return NULL;
1543     }
1544 
1545     size_t file_size = (size_t)file_size_signed;
1546     void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1547     if (file_data == NULL)
1548     {
1549         fclose(f);
1550         return NULL;
1551     }
1552     if (fread(file_data, 1, file_size, f) != file_size)
1553     {
1554         fclose(f);
1555         ImGui::MemFree(file_data);
1556         return NULL;
1557     }
1558     if (padding_bytes > 0)
1559         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1560 
1561     fclose(f);
1562     if (out_file_size)
1563         *out_file_size = file_size;
1564 
1565     return file_data;
1566 }
1567 
1568 //-----------------------------------------------------------------------------
1569 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1570 //-----------------------------------------------------------------------------
1571 
1572 // Convert UTF-8 to 32-bits character, process single character input.
1573 // Based on stb_from_utf8() from github.com/nothings/stb/
1574 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1575 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1576 {
1577     unsigned int c = (unsigned int)-1;
1578     const unsigned char* str = (const unsigned char*)in_text;
1579     if (!(*str & 0x80))
1580     {
1581         c = (unsigned int)(*str++);
1582         *out_char = c;
1583         return 1;
1584     }
1585     if ((*str & 0xe0) == 0xc0)
1586     {
1587         *out_char = 0xFFFD; // will be invalid but not end of string
1588         if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1589         if (*str < 0xc2) return 2;
1590         c = (unsigned int)((*str++ & 0x1f) << 6);
1591         if ((*str & 0xc0) != 0x80) return 2;
1592         c += (*str++ & 0x3f);
1593         *out_char = c;
1594         return 2;
1595     }
1596     if ((*str & 0xf0) == 0xe0)
1597     {
1598         *out_char = 0xFFFD; // will be invalid but not end of string
1599         if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1600         if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1601         if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1602         c = (unsigned int)((*str++ & 0x0f) << 12);
1603         if ((*str & 0xc0) != 0x80) return 3;
1604         c += (unsigned int)((*str++ & 0x3f) << 6);
1605         if ((*str & 0xc0) != 0x80) return 3;
1606         c += (*str++ & 0x3f);
1607         *out_char = c;
1608         return 3;
1609     }
1610     if ((*str & 0xf8) == 0xf0)
1611     {
1612         *out_char = 0xFFFD; // will be invalid but not end of string
1613         if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1614         if (*str > 0xf4) return 4;
1615         if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1616         if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1617         c = (unsigned int)((*str++ & 0x07) << 18);
1618         if ((*str & 0xc0) != 0x80) return 4;
1619         c += (unsigned int)((*str++ & 0x3f) << 12);
1620         if ((*str & 0xc0) != 0x80) return 4;
1621         c += (unsigned int)((*str++ & 0x3f) << 6);
1622         if ((*str & 0xc0) != 0x80) return 4;
1623         c += (*str++ & 0x3f);
1624         // utf-8 encodings of values used in surrogate pairs are invalid
1625         if ((c & 0xFFFFF800) == 0xD800) return 4;
1626         *out_char = c;
1627         return 4;
1628     }
1629     *out_char = 0;
1630     return 0;
1631 }
1632 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1633 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1634 {
1635     ImWchar* buf_out = buf;
1636     ImWchar* buf_end = buf + buf_size;
1637     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1638     {
1639         unsigned int c;
1640         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1641         if (c == 0)
1642             break;
1643         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
1644             *buf_out++ = (ImWchar)c;
1645     }
1646     *buf_out = 0;
1647     if (in_text_remaining)
1648         *in_text_remaining = in_text;
1649     return (int)(buf_out - buf);
1650 }
1651 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1652 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1653 {
1654     int char_count = 0;
1655     while ((!in_text_end || in_text < in_text_end) && *in_text)
1656     {
1657         unsigned int c;
1658         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1659         if (c == 0)
1660             break;
1661         if (c < 0x10000)
1662             char_count++;
1663     }
1664     return char_count;
1665 }
1666 
1667 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1668 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1669 {
1670     if (c < 0x80)
1671     {
1672         buf[0] = (char)c;
1673         return 1;
1674     }
1675     if (c < 0x800)
1676     {
1677         if (buf_size < 2) return 0;
1678         buf[0] = (char)(0xc0 + (c >> 6));
1679         buf[1] = (char)(0x80 + (c & 0x3f));
1680         return 2;
1681     }
1682     if (c >= 0xdc00 && c < 0xe000)
1683     {
1684         return 0;
1685     }
1686     if (c >= 0xd800 && c < 0xdc00)
1687     {
1688         if (buf_size < 4) return 0;
1689         buf[0] = (char)(0xf0 + (c >> 18));
1690         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1691         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1692         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1693         return 4;
1694     }
1695     //else if (c < 0x10000)
1696     {
1697         if (buf_size < 3) return 0;
1698         buf[0] = (char)(0xe0 + (c >> 12));
1699         buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1700         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1701         return 3;
1702     }
1703 }
1704 
1705 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1706 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1707 {
1708     unsigned int dummy = 0;
1709     return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1710 }
1711 
ImTextCountUtf8BytesFromChar(unsigned int c)1712 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1713 {
1714     if (c < 0x80) return 1;
1715     if (c < 0x800) return 2;
1716     if (c >= 0xdc00 && c < 0xe000) return 0;
1717     if (c >= 0xd800 && c < 0xdc00) return 4;
1718     return 3;
1719 }
1720 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1721 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1722 {
1723     char* buf_out = buf;
1724     const char* buf_end = buf + buf_size;
1725     while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1726     {
1727         unsigned int c = (unsigned int)(*in_text++);
1728         if (c < 0x80)
1729             *buf_out++ = (char)c;
1730         else
1731             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1732     }
1733     *buf_out = 0;
1734     return (int)(buf_out - buf);
1735 }
1736 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1737 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1738 {
1739     int bytes_count = 0;
1740     while ((!in_text_end || in_text < in_text_end) && *in_text)
1741     {
1742         unsigned int c = (unsigned int)(*in_text++);
1743         if (c < 0x80)
1744             bytes_count++;
1745         else
1746             bytes_count += ImTextCountUtf8BytesFromChar(c);
1747     }
1748     return bytes_count;
1749 }
1750 
1751 //-----------------------------------------------------------------------------
1752 // [SECTION] MISC HELPER/UTILTIES (Color functions)
1753 // Note: The Convert functions are early design which are not consistent with other API.
1754 //-----------------------------------------------------------------------------
1755 
ColorConvertU32ToFloat4(ImU32 in)1756 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1757 {
1758     float s = 1.0f/255.0f;
1759     return ImVec4(
1760         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1761         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1762         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1763         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1764 }
1765 
ColorConvertFloat4ToU32(const ImVec4 & in)1766 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1767 {
1768     ImU32 out;
1769     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1770     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1771     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1772     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1773     return out;
1774 }
1775 
1776 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1777 // 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)1778 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1779 {
1780     float K = 0.f;
1781     if (g < b)
1782     {
1783         ImSwap(g, b);
1784         K = -1.f;
1785     }
1786     if (r < g)
1787     {
1788         ImSwap(r, g);
1789         K = -2.f / 6.f - K;
1790     }
1791 
1792     const float chroma = r - (g < b ? g : b);
1793     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1794     out_s = chroma / (r + 1e-20f);
1795     out_v = r;
1796 }
1797 
1798 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1799 // 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)1800 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1801 {
1802     if (s == 0.0f)
1803     {
1804         // gray
1805         out_r = out_g = out_b = v;
1806         return;
1807     }
1808 
1809     h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1810     int   i = (int)h;
1811     float f = h - (float)i;
1812     float p = v * (1.0f - s);
1813     float q = v * (1.0f - s * f);
1814     float t = v * (1.0f - s * (1.0f - f));
1815 
1816     switch (i)
1817     {
1818     case 0: out_r = v; out_g = t; out_b = p; break;
1819     case 1: out_r = q; out_g = v; out_b = p; break;
1820     case 2: out_r = p; out_g = v; out_b = t; break;
1821     case 3: out_r = p; out_g = q; out_b = v; break;
1822     case 4: out_r = t; out_g = p; out_b = v; break;
1823     case 5: default: out_r = v; out_g = p; out_b = q; break;
1824     }
1825 }
1826 
GetColorU32(ImGuiCol idx,float alpha_mul)1827 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1828 {
1829     ImGuiStyle& style = GImGui->Style;
1830     ImVec4 c = style.Colors[idx];
1831     c.w *= style.Alpha * alpha_mul;
1832     return ColorConvertFloat4ToU32(c);
1833 }
1834 
GetColorU32(const ImVec4 & col)1835 ImU32 ImGui::GetColorU32(const ImVec4& col)
1836 {
1837     ImGuiStyle& style = GImGui->Style;
1838     ImVec4 c = col;
1839     c.w *= style.Alpha;
1840     return ColorConvertFloat4ToU32(c);
1841 }
1842 
GetStyleColorVec4(ImGuiCol idx)1843 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1844 {
1845     ImGuiStyle& style = GImGui->Style;
1846     return style.Colors[idx];
1847 }
1848 
GetColorU32(ImU32 col)1849 ImU32 ImGui::GetColorU32(ImU32 col)
1850 {
1851     float style_alpha = GImGui->Style.Alpha;
1852     if (style_alpha >= 1.0f)
1853         return col;
1854     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1855     a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1856     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1857 }
1858 
1859 //-----------------------------------------------------------------------------
1860 // [SECTION] ImGuiStorage
1861 // Helper: Key->value storage
1862 //-----------------------------------------------------------------------------
1863 
1864 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1865 static ImGuiStorage::Pair* LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1866 {
1867     ImGuiStorage::Pair* first = data.Data;
1868     ImGuiStorage::Pair* last = data.Data + data.Size;
1869     size_t count = (size_t)(last - first);
1870     while (count > 0)
1871     {
1872         size_t count2 = count >> 1;
1873         ImGuiStorage::Pair* mid = first + count2;
1874         if (mid->key < key)
1875         {
1876             first = ++mid;
1877             count -= count2 + 1;
1878         }
1879         else
1880         {
1881             count = count2;
1882         }
1883     }
1884     return first;
1885 }
1886 
1887 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1888 void ImGuiStorage::BuildSortByKey()
1889 {
1890     struct StaticFunc
1891     {
1892         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1893         {
1894             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1895             if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1896             if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1897             return 0;
1898         }
1899     };
1900     if (Data.Size > 1)
1901         ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1902 }
1903 
GetInt(ImGuiID key,int default_val) const1904 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1905 {
1906     ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1907     if (it == Data.end() || it->key != key)
1908         return default_val;
1909     return it->val_i;
1910 }
1911 
GetBool(ImGuiID key,bool default_val) const1912 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1913 {
1914     return GetInt(key, default_val ? 1 : 0) != 0;
1915 }
1916 
GetFloat(ImGuiID key,float default_val) const1917 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1918 {
1919     ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1920     if (it == Data.end() || it->key != key)
1921         return default_val;
1922     return it->val_f;
1923 }
1924 
GetVoidPtr(ImGuiID key) const1925 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1926 {
1927     ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1928     if (it == Data.end() || it->key != key)
1929         return NULL;
1930     return it->val_p;
1931 }
1932 
1933 // 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)1934 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1935 {
1936     ImGuiStorage::Pair* it = LowerBound(Data, key);
1937     if (it == Data.end() || it->key != key)
1938         it = Data.insert(it, Pair(key, default_val));
1939     return &it->val_i;
1940 }
1941 
GetBoolRef(ImGuiID key,bool default_val)1942 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1943 {
1944     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1945 }
1946 
GetFloatRef(ImGuiID key,float default_val)1947 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1948 {
1949     ImGuiStorage::Pair* it = LowerBound(Data, key);
1950     if (it == Data.end() || it->key != key)
1951         it = Data.insert(it, Pair(key, default_val));
1952     return &it->val_f;
1953 }
1954 
GetVoidPtrRef(ImGuiID key,void * default_val)1955 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1956 {
1957     ImGuiStorage::Pair* it = LowerBound(Data, key);
1958     if (it == Data.end() || it->key != key)
1959         it = Data.insert(it, Pair(key, default_val));
1960     return &it->val_p;
1961 }
1962 
1963 // 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)1964 void ImGuiStorage::SetInt(ImGuiID key, int val)
1965 {
1966     ImGuiStorage::Pair* it = LowerBound(Data, key);
1967     if (it == Data.end() || it->key != key)
1968     {
1969         Data.insert(it, Pair(key, val));
1970         return;
1971     }
1972     it->val_i = val;
1973 }
1974 
SetBool(ImGuiID key,bool val)1975 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1976 {
1977     SetInt(key, val ? 1 : 0);
1978 }
1979 
SetFloat(ImGuiID key,float val)1980 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1981 {
1982     ImGuiStorage::Pair* it = LowerBound(Data, key);
1983     if (it == Data.end() || it->key != key)
1984     {
1985         Data.insert(it, Pair(key, val));
1986         return;
1987     }
1988     it->val_f = val;
1989 }
1990 
SetVoidPtr(ImGuiID key,void * val)1991 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1992 {
1993     ImGuiStorage::Pair* it = LowerBound(Data, key);
1994     if (it == Data.end() || it->key != key)
1995     {
1996         Data.insert(it, Pair(key, val));
1997         return;
1998     }
1999     it->val_p = val;
2000 }
2001 
SetAllInt(int v)2002 void ImGuiStorage::SetAllInt(int v)
2003 {
2004     for (int i = 0; i < Data.Size; i++)
2005         Data[i].val_i = v;
2006 }
2007 
2008 //-----------------------------------------------------------------------------
2009 // [SECTION] ImGuiTextFilter
2010 //-----------------------------------------------------------------------------
2011 
2012 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)2013 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
2014 {
2015     if (default_filter)
2016     {
2017         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2018         Build();
2019     }
2020     else
2021     {
2022         InputBuf[0] = 0;
2023         CountGrep = 0;
2024     }
2025 }
2026 
Draw(const char * label,float width)2027 bool ImGuiTextFilter::Draw(const char* label, float width)
2028 {
2029     if (width != 0.0f)
2030         ImGui::PushItemWidth(width);
2031     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2032     if (width != 0.0f)
2033         ImGui::PopItemWidth();
2034     if (value_changed)
2035         Build();
2036     return value_changed;
2037 }
2038 
split(char separator,ImVector<TextRange> * out) const2039 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
2040 {
2041     out->resize(0);
2042     const char* wb = b;
2043     const char* we = wb;
2044     while (we < e)
2045     {
2046         if (*we == separator)
2047         {
2048             out->push_back(TextRange(wb, we));
2049             wb = we + 1;
2050         }
2051         we++;
2052     }
2053     if (wb != we)
2054         out->push_back(TextRange(wb, we));
2055 }
2056 
Build()2057 void ImGuiTextFilter::Build()
2058 {
2059     Filters.resize(0);
2060     TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
2061     input_range.split(',', &Filters);
2062 
2063     CountGrep = 0;
2064     for (int i = 0; i != Filters.Size; i++)
2065     {
2066         TextRange& f = Filters[i];
2067         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2068             f.b++;
2069         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2070             f.e--;
2071         if (f.empty())
2072             continue;
2073         if (Filters[i].b[0] != '-')
2074             CountGrep += 1;
2075     }
2076 }
2077 
PassFilter(const char * text,const char * text_end) const2078 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2079 {
2080     if (Filters.empty())
2081         return true;
2082 
2083     if (text == NULL)
2084         text = "";
2085 
2086     for (int i = 0; i != Filters.Size; i++)
2087     {
2088         const TextRange& f = Filters[i];
2089         if (f.empty())
2090             continue;
2091         if (f.b[0] == '-')
2092         {
2093             // Subtract
2094             if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
2095                 return false;
2096         }
2097         else
2098         {
2099             // Grep
2100             if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
2101                 return true;
2102         }
2103     }
2104 
2105     // Implicit * grep
2106     if (CountGrep == 0)
2107         return true;
2108 
2109     return false;
2110 }
2111 
2112 //-----------------------------------------------------------------------------
2113 // [SECTION] ImGuiTextBuffer
2114 //-----------------------------------------------------------------------------
2115 
2116 // On some platform vsnprintf() takes va_list by reference and modifies it.
2117 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2118 #ifndef va_copy
2119 #if defined(__GNUC__) || defined(__clang__)
2120 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2121 #else
2122 #define va_copy(dest, src) (dest = src)
2123 #endif
2124 #endif
2125 
2126 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2127 
append(const char * str,const char * str_end)2128 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2129 {
2130     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2131 
2132     // Add zero-terminator the first time
2133     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2134     const int needed_sz = write_off + len;
2135     if (write_off + len >= Buf.Capacity)
2136     {
2137         int new_capacity = Buf.Capacity * 2;
2138         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2139     }
2140 
2141     Buf.resize(needed_sz);
2142     memcpy(&Buf[write_off - 1], str, (size_t)len);
2143     Buf[write_off - 1 + len] = 0;
2144 }
2145 
appendf(const char * fmt,...)2146 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2147 {
2148     va_list args;
2149     va_start(args, fmt);
2150     appendfv(fmt, args);
2151     va_end(args);
2152 }
2153 
2154 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2155 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2156 {
2157     va_list args_copy;
2158     va_copy(args_copy, args);
2159 
2160     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2161     if (len <= 0)
2162     {
2163         va_end(args_copy);
2164         return;
2165     }
2166 
2167     // Add zero-terminator the first time
2168     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2169     const int needed_sz = write_off + len;
2170     if (write_off + len >= Buf.Capacity)
2171     {
2172         int new_capacity = Buf.Capacity * 2;
2173         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2174     }
2175 
2176     Buf.resize(needed_sz);
2177     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2178     va_end(args_copy);
2179 }
2180 
2181 //-----------------------------------------------------------------------------
2182 // [SECTION] ImGuiListClipper
2183 // This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
2184 //-----------------------------------------------------------------------------
2185 
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2186 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2187 {
2188     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2189     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2190     // The clipper should probably have a 4th step to display the last item in a regular manner.
2191     ImGui::SetCursorPosY(pos_y);
2192     ImGuiWindow* window = ImGui::GetCurrentWindow();
2193     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.
2194     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.
2195     if (window->DC.ColumnsSet)
2196         window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;           // Setting this so that cell Y position are set properly
2197 }
2198 
2199 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2200 // Use case B: Begin() called from constructor with items_height>0
2201 // 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)2202 void ImGuiListClipper::Begin(int count, float items_height)
2203 {
2204     StartPosY = ImGui::GetCursorPosY();
2205     ItemsHeight = items_height;
2206     ItemsCount = count;
2207     StepNo = 0;
2208     DisplayEnd = DisplayStart = -1;
2209     if (ItemsHeight > 0.0f)
2210     {
2211         ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2212         if (DisplayStart > 0)
2213             SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2214         StepNo = 2;
2215     }
2216 }
2217 
End()2218 void ImGuiListClipper::End()
2219 {
2220     if (ItemsCount < 0)
2221         return;
2222     // 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.
2223     if (ItemsCount < INT_MAX)
2224         SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2225     ItemsCount = -1;
2226     StepNo = 3;
2227 }
2228 
Step()2229 bool ImGuiListClipper::Step()
2230 {
2231     if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2232     {
2233         ItemsCount = -1;
2234         return false;
2235     }
2236     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.
2237     {
2238         DisplayStart = 0;
2239         DisplayEnd = 1;
2240         StartPosY = ImGui::GetCursorPosY();
2241         StepNo = 1;
2242         return true;
2243     }
2244     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.
2245     {
2246         if (ItemsCount == 1) { ItemsCount = -1; return false; }
2247         float items_height = ImGui::GetCursorPosY() - StartPosY;
2248         IM_ASSERT(items_height > 0.0f);   // If this triggers, it means Item 0 hasn't moved the cursor vertically
2249         Begin(ItemsCount-1, items_height);
2250         DisplayStart++;
2251         DisplayEnd++;
2252         StepNo = 3;
2253         return true;
2254     }
2255     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.
2256     {
2257         IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2258         StepNo = 3;
2259         return true;
2260     }
2261     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.
2262         End();
2263     return false;
2264 }
2265 
2266 //-----------------------------------------------------------------------------
2267 // [SECTION] RENDER HELPERS
2268 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2269 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2270 //-----------------------------------------------------------------------------
2271 
FindRenderedTextEnd(const char * text,const char * text_end)2272 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2273 {
2274     const char* text_display_end = text;
2275     if (!text_end)
2276         text_end = (const char*)-1;
2277 
2278     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2279         text_display_end++;
2280     return text_display_end;
2281 }
2282 
2283 // Internal ImGui functions to render text
2284 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2285 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2286 {
2287     ImGuiContext& g = *GImGui;
2288     ImGuiWindow* window = g.CurrentWindow;
2289 
2290     // Hide anything after a '##' string
2291     const char* text_display_end;
2292     if (hide_text_after_hash)
2293     {
2294         text_display_end = FindRenderedTextEnd(text, text_end);
2295     }
2296     else
2297     {
2298         if (!text_end)
2299             text_end = text + strlen(text); // FIXME-OPT
2300         text_display_end = text_end;
2301     }
2302 
2303     if (text != text_display_end)
2304     {
2305         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2306         if (g.LogEnabled)
2307             LogRenderedText(&pos, text, text_display_end);
2308     }
2309 }
2310 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2311 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2312 {
2313     ImGuiContext& g = *GImGui;
2314     ImGuiWindow* window = g.CurrentWindow;
2315 
2316     if (!text_end)
2317         text_end = text + strlen(text); // FIXME-OPT
2318 
2319     if (text != text_end)
2320     {
2321         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2322         if (g.LogEnabled)
2323             LogRenderedText(&pos, text, text_end);
2324     }
2325 }
2326 
2327 // Default clip_rect uses (pos_min,pos_max)
2328 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
RenderTextClippedEx(ImDrawList * draw_list,const ImVec2 & pos_min,const ImVec2 & pos_max,const char * text,const char * text_display_end,const ImVec2 * text_size_if_known,const ImVec2 & align,const ImRect * clip_rect)2329 void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2330 {
2331     // Perform CPU side clipping for single clipped element to avoid using scissor state
2332     ImVec2 pos = pos_min;
2333     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2334 
2335     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2336     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2337     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2338     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2339         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2340 
2341     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2342     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2343     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2344 
2345     // Render
2346     if (need_clipping)
2347     {
2348         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2349         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2350     }
2351     else
2352     {
2353         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2354     }
2355 }
2356 
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)2357 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)
2358 {
2359     // Hide anything after a '##' string
2360     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2361     const int text_len = (int)(text_display_end - text);
2362     if (text_len == 0)
2363         return;
2364 
2365     ImGuiContext& g = *GImGui;
2366     ImGuiWindow* window = g.CurrentWindow;
2367     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2368     if (g.LogEnabled)
2369         LogRenderedText(&pos_min, text, text_display_end);
2370 }
2371 
2372 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2373 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2374 {
2375     ImGuiContext& g = *GImGui;
2376     ImGuiWindow* window = g.CurrentWindow;
2377     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2378     const float border_size = g.Style.FrameBorderSize;
2379     if (border && border_size > 0.0f)
2380     {
2381         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2382         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2383     }
2384 }
2385 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2386 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2387 {
2388     ImGuiContext& g = *GImGui;
2389     ImGuiWindow* window = g.CurrentWindow;
2390     const float border_size = g.Style.FrameBorderSize;
2391     if (border_size > 0.0f)
2392     {
2393         window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2394         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2395     }
2396 }
2397 
2398 // 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)2399 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2400 {
2401     ImGuiContext& g = *GImGui;
2402 
2403     const float h = g.FontSize * 1.00f;
2404     float r = h * 0.40f * scale;
2405     ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2406 
2407     ImVec2 a, b, c;
2408     switch (dir)
2409     {
2410     case ImGuiDir_Up:
2411     case ImGuiDir_Down:
2412         if (dir == ImGuiDir_Up) r = -r;
2413         a = ImVec2(+0.000f,+0.750f) * r;
2414         b = ImVec2(-0.866f,-0.750f) * r;
2415         c = ImVec2(+0.866f,-0.750f) * r;
2416         break;
2417     case ImGuiDir_Left:
2418     case ImGuiDir_Right:
2419         if (dir == ImGuiDir_Left) r = -r;
2420         a = ImVec2(+0.750f,+0.000f) * r;
2421         b = ImVec2(-0.750f,+0.866f) * r;
2422         c = ImVec2(-0.750f,-0.866f) * r;
2423         break;
2424     case ImGuiDir_None:
2425     case ImGuiDir_COUNT:
2426         IM_ASSERT(0);
2427         break;
2428     }
2429 
2430     g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
2431 }
2432 
RenderBullet(ImVec2 pos)2433 void ImGui::RenderBullet(ImVec2 pos)
2434 {
2435     ImGuiContext& g = *GImGui;
2436     ImGuiWindow* window = g.CurrentWindow;
2437     window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
2438 }
2439 
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2440 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2441 {
2442     ImGuiContext& g = *GImGui;
2443     ImGuiWindow* window = g.CurrentWindow;
2444 
2445     float thickness = ImMax(sz / 5.0f, 1.0f);
2446     sz -= thickness*0.5f;
2447     pos += ImVec2(thickness*0.25f, thickness*0.25f);
2448 
2449     float third = sz / 3.0f;
2450     float bx = pos.x + third;
2451     float by = pos.y + sz - third*0.5f;
2452     window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2453     window->DrawList->PathLineTo(ImVec2(bx, by));
2454     window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2455     window->DrawList->PathStroke(col, false, thickness);
2456 }
2457 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2458 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2459 {
2460     ImGuiContext& g = *GImGui;
2461     if (id != g.NavId)
2462         return;
2463     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2464         return;
2465     ImGuiWindow* window = g.CurrentWindow;
2466     if (window->DC.NavHideHighlightOneFrame)
2467         return;
2468 
2469     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2470     ImRect display_rect = bb;
2471     display_rect.ClipWith(window->ClipRect);
2472     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2473     {
2474         const float THICKNESS = 2.0f;
2475         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2476         display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2477         bool fully_visible = window->ClipRect.Contains(display_rect);
2478         if (!fully_visible)
2479             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2480         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);
2481         if (!fully_visible)
2482             window->DrawList->PopClipRect();
2483     }
2484     if (flags & ImGuiNavHighlightFlags_TypeThin)
2485     {
2486         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2487     }
2488 }
2489 
2490 //-----------------------------------------------------------------------------
2491 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2492 //-----------------------------------------------------------------------------
2493 
2494 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2495 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2496     : DrawListInst(&context->DrawListSharedData)
2497 {
2498     Name = ImStrdup(name);
2499     ID = ImHashStr(name, 0);
2500     IDStack.push_back(ID);
2501     Flags = ImGuiWindowFlags_None;
2502     Pos = ImVec2(0.0f, 0.0f);
2503     Size = SizeFull = ImVec2(0.0f, 0.0f);
2504     SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2505     WindowPadding = ImVec2(0.0f, 0.0f);
2506     WindowRounding = 0.0f;
2507     WindowBorderSize = 0.0f;
2508     NameBufLen = (int)strlen(name) + 1;
2509     MoveId = GetID("#MOVE");
2510     ChildId = 0;
2511     Scroll = ImVec2(0.0f, 0.0f);
2512     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2513     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2514     ScrollbarSizes = ImVec2(0.0f, 0.0f);
2515     ScrollbarX = ScrollbarY = false;
2516     Active = WasActive = false;
2517     WriteAccessed = false;
2518     Collapsed = false;
2519     WantCollapseToggle = false;
2520     SkipItems = false;
2521     Appearing = false;
2522     Hidden = false;
2523     HasCloseButton = false;
2524     ResizeBorderHeld = -1;
2525     BeginCount = 0;
2526     BeginOrderWithinParent = -1;
2527     BeginOrderWithinContext = -1;
2528     PopupId = 0;
2529     AutoFitFramesX = AutoFitFramesY = -1;
2530     AutoFitOnlyGrows = false;
2531     AutoFitChildAxises = 0x00;
2532     AutoPosLastDirection = ImGuiDir_None;
2533     HiddenFramesRegular = HiddenFramesForResize = 0;
2534     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2535     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2536 
2537     LastFrameActive = -1;
2538     ItemWidthDefault = 0.0f;
2539     FontWindowScale = 1.0f;
2540     SettingsIdx = -1;
2541 
2542     DrawList = &DrawListInst;
2543     DrawList->_OwnerName = Name;
2544     ParentWindow = NULL;
2545     RootWindow = NULL;
2546     RootWindowForTitleBarHighlight = NULL;
2547     RootWindowForNav = NULL;
2548 
2549     NavLastIds[0] = NavLastIds[1] = 0;
2550     NavRectRel[0] = NavRectRel[1] = ImRect();
2551     NavLastChildNavWindow = NULL;
2552 
2553     FocusIdxAllCounter = FocusIdxTabCounter = -1;
2554     FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2555     FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2556 }
2557 
~ImGuiWindow()2558 ImGuiWindow::~ImGuiWindow()
2559 {
2560     IM_ASSERT(DrawList == &DrawListInst);
2561     IM_DELETE(Name);
2562     for (int i = 0; i != ColumnsStorage.Size; i++)
2563         ColumnsStorage[i].~ImGuiColumnsSet();
2564 }
2565 
GetID(const char * str,const char * str_end)2566 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2567 {
2568     ImGuiID seed = IDStack.back();
2569     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2570     ImGui::KeepAliveID(id);
2571     return id;
2572 }
2573 
GetID(const void * ptr)2574 ImGuiID ImGuiWindow::GetID(const void* ptr)
2575 {
2576     ImGuiID seed = IDStack.back();
2577     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2578     ImGui::KeepAliveID(id);
2579     return id;
2580 }
2581 
GetIDNoKeepAlive(const char * str,const char * str_end)2582 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2583 {
2584     ImGuiID seed = IDStack.back();
2585     return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2586 }
2587 
GetIDNoKeepAlive(const void * ptr)2588 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2589 {
2590     ImGuiID seed = IDStack.back();
2591     return ImHashData(&ptr, sizeof(void*), seed);
2592 }
2593 
2594 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2595 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2596 {
2597     ImGuiID seed = IDStack.back();
2598     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) };
2599     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2600     ImGui::KeepAliveID(id);
2601     return id;
2602 }
2603 
SetCurrentWindow(ImGuiWindow * window)2604 static void SetCurrentWindow(ImGuiWindow* window)
2605 {
2606     ImGuiContext& g = *GImGui;
2607     g.CurrentWindow = window;
2608     if (window)
2609         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2610 }
2611 
SetNavID(ImGuiID id,int nav_layer)2612 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2613 {
2614     ImGuiContext& g = *GImGui;
2615     IM_ASSERT(g.NavWindow);
2616     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2617     g.NavId = id;
2618     g.NavWindow->NavLastIds[nav_layer] = id;
2619 }
2620 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2621 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2622 {
2623     ImGuiContext& g = *GImGui;
2624     SetNavID(id, nav_layer);
2625     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2626     g.NavMousePosDirty = true;
2627     g.NavDisableHighlight = false;
2628     g.NavDisableMouseHover = true;
2629 }
2630 
SetActiveID(ImGuiID id,ImGuiWindow * window)2631 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2632 {
2633     ImGuiContext& g = *GImGui;
2634     g.ActiveIdIsJustActivated = (g.ActiveId != id);
2635     if (g.ActiveIdIsJustActivated)
2636     {
2637         g.ActiveIdTimer = 0.0f;
2638         g.ActiveIdHasBeenPressed = false;
2639         g.ActiveIdHasBeenEdited = false;
2640         if (id != 0)
2641         {
2642             g.LastActiveId = id;
2643             g.LastActiveIdTimer = 0.0f;
2644         }
2645     }
2646     g.ActiveId = id;
2647     g.ActiveIdAllowNavDirFlags = 0;
2648     g.ActiveIdBlockNavInputFlags = 0;
2649     g.ActiveIdAllowOverlap = false;
2650     g.ActiveIdWindow = window;
2651     if (id)
2652     {
2653         g.ActiveIdIsAlive = id;
2654         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2655     }
2656 }
2657 
2658 // FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
SetFocusID(ImGuiID id,ImGuiWindow * window)2659 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2660 {
2661     ImGuiContext& g = *GImGui;
2662     IM_ASSERT(id != 0);
2663 
2664     // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2665     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2666     if (g.NavWindow != window)
2667         g.NavInitRequest = false;
2668     g.NavId = id;
2669     g.NavWindow = window;
2670     g.NavLayer = nav_layer;
2671     window->NavLastIds[nav_layer] = id;
2672     if (window->DC.LastItemId == id)
2673         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2674 
2675     if (g.ActiveIdSource == ImGuiInputSource_Nav)
2676         g.NavDisableMouseHover = true;
2677     else
2678         g.NavDisableHighlight = true;
2679 }
2680 
ClearActiveID()2681 void ImGui::ClearActiveID()
2682 {
2683     SetActiveID(0, NULL);
2684 }
2685 
SetHoveredID(ImGuiID id)2686 void ImGui::SetHoveredID(ImGuiID id)
2687 {
2688     ImGuiContext& g = *GImGui;
2689     g.HoveredId = id;
2690     g.HoveredIdAllowOverlap = false;
2691     if (id != 0 && g.HoveredIdPreviousFrame != id)
2692         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2693 }
2694 
GetHoveredID()2695 ImGuiID ImGui::GetHoveredID()
2696 {
2697     ImGuiContext& g = *GImGui;
2698     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2699 }
2700 
KeepAliveID(ImGuiID id)2701 void ImGui::KeepAliveID(ImGuiID id)
2702 {
2703     ImGuiContext& g = *GImGui;
2704     if (g.ActiveId == id)
2705         g.ActiveIdIsAlive = id;
2706     if (g.ActiveIdPreviousFrame == id)
2707         g.ActiveIdPreviousFrameIsAlive = true;
2708 }
2709 
MarkItemEdited(ImGuiID id)2710 void ImGui::MarkItemEdited(ImGuiID id)
2711 {
2712     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2713     // 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.
2714     ImGuiContext& g = *GImGui;
2715     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2716     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2717     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2718     g.ActiveIdHasBeenEdited = true;
2719     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2720 }
2721 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2722 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2723 {
2724     // An active popup disable hovering on other windows (apart from its own children)
2725     // FIXME-OPT: This could be cached/stored within the window.
2726     ImGuiContext& g = *GImGui;
2727     if (g.NavWindow)
2728         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2729             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2730             {
2731                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2732                 // NB: The order of those two tests is important because Modal windows are also Popups.
2733                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2734                     return false;
2735                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2736                     return false;
2737             }
2738 
2739     return true;
2740 }
2741 
2742 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2743 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2744 {
2745     ImGuiContext& g = *GImGui;
2746     ImGuiWindow* window = g.CurrentWindow;
2747     if (window->SkipItems)
2748         return;
2749 
2750     // Always align ourselves on pixel boundaries
2751     const float line_height = ImMax(window->DC.CurrentLineSize.y, size.y);
2752     const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2753     //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]
2754     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2755     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
2756     window->DC.CursorPos.y = (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);
2757     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2758     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2759     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2760 
2761     window->DC.PrevLineSize.y = line_height;
2762     window->DC.PrevLineTextBaseOffset = text_base_offset;
2763     window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2764 
2765     // Horizontal layout mode
2766     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2767         SameLine();
2768 }
2769 
ItemSize(const ImRect & bb,float text_offset_y)2770 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2771 {
2772     ItemSize(bb.GetSize(), text_offset_y);
2773 }
2774 
2775 // Declare item bounding box for clipping and interaction.
2776 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2777 // 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)2778 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2779 {
2780     ImGuiContext& g = *GImGui;
2781     ImGuiWindow* window = g.CurrentWindow;
2782 
2783     if (id != 0)
2784     {
2785         // Navigation processing runs prior to clipping early-out
2786         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2787         //  (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.
2788         //      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.
2789         //      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)
2790         window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2791         if (g.NavId == id || g.NavAnyRequest)
2792             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2793                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2794                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2795     }
2796 
2797     window->DC.LastItemId = id;
2798     window->DC.LastItemRect = bb;
2799     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2800 
2801 #ifdef IMGUI_ENABLE_TEST_ENGINE
2802     if (id != 0)
2803         ImGuiTestEngineHook_ItemAdd(&g, nav_bb_arg ? *nav_bb_arg : bb, id);
2804 #endif
2805 
2806     // Clipping test
2807     const bool is_clipped = IsClippedEx(bb, id, false);
2808     if (is_clipped)
2809         return false;
2810     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2811 
2812     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2813     if (IsMouseHoveringRect(bb.Min, bb.Max))
2814         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2815     return true;
2816 }
2817 
2818 // This is roughly matching the behavior of internal-facing ItemHoverable()
2819 // - 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()
2820 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2821 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2822 {
2823     ImGuiContext& g = *GImGui;
2824     ImGuiWindow* window = g.CurrentWindow;
2825     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2826         return IsItemFocused();
2827 
2828     // Test for bounding box overlap, as updated as ItemAdd()
2829     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2830         return false;
2831     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
2832 
2833     // Test if we are hovering the right window (our window could be behind another window)
2834     // [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.
2835     // 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.
2836     //if (g.HoveredWindow != window)
2837     //    return false;
2838     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2839         return false;
2840 
2841     // Test if another item is active (e.g. being dragged)
2842     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2843         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2844             return false;
2845 
2846     // Test if interactions on this window are blocked by an active popup or modal
2847     if (!IsWindowContentHoverable(window, flags))
2848         return false;
2849 
2850     // Test if the item is disabled
2851     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2852         return false;
2853 
2854     // Special handling for the dummy item after Begin() which represent the title bar or tab.
2855     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2856     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2857         return false;
2858     return true;
2859 }
2860 
2861 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2862 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2863 {
2864     ImGuiContext& g = *GImGui;
2865     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2866         return false;
2867 
2868     ImGuiWindow* window = g.CurrentWindow;
2869     if (g.HoveredWindow != window)
2870         return false;
2871     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2872         return false;
2873     if (!IsMouseHoveringRect(bb.Min, bb.Max))
2874         return false;
2875     if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2876         return false;
2877     if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2878         return false;
2879 
2880     SetHoveredID(id);
2881     return true;
2882 }
2883 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2884 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2885 {
2886     ImGuiContext& g = *GImGui;
2887     ImGuiWindow* window = g.CurrentWindow;
2888     if (!bb.Overlaps(window->ClipRect))
2889         if (id == 0 || id != g.ActiveId)
2890             if (clip_even_when_logged || !g.LogEnabled)
2891                 return true;
2892     return false;
2893 }
2894 
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2895 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2896 {
2897     ImGuiContext& g = *GImGui;
2898 
2899     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2900     window->FocusIdxAllCounter++;
2901     if (is_tab_stop)
2902         window->FocusIdxTabCounter++;
2903 
2904     // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2905     // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2906     if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2907         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.
2908 
2909     if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2910         return true;
2911     if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2912     {
2913         g.NavJustTabbedId = id;
2914         return true;
2915     }
2916 
2917     return false;
2918 }
2919 
FocusableItemUnregister(ImGuiWindow * window)2920 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2921 {
2922     window->FocusIdxAllCounter--;
2923     window->FocusIdxTabCounter--;
2924 }
2925 
CalcItemSize(ImVec2 size,float default_x,float default_y)2926 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2927 {
2928     ImGuiContext& g = *GImGui;
2929     ImVec2 content_max;
2930     if (size.x < 0.0f || size.y < 0.0f)
2931         content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2932     if (size.x <= 0.0f)
2933         size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2934     if (size.y <= 0.0f)
2935         size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2936     return size;
2937 }
2938 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2939 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2940 {
2941     if (wrap_pos_x < 0.0f)
2942         return 0.0f;
2943 
2944     ImGuiWindow* window = GetCurrentWindowRead();
2945     if (wrap_pos_x == 0.0f)
2946         wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2947     else if (wrap_pos_x > 0.0f)
2948         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2949 
2950     return ImMax(wrap_pos_x - pos.x, 1.0f);
2951 }
2952 
MemAlloc(size_t size)2953 void* ImGui::MemAlloc(size_t size)
2954 {
2955     if (ImGuiContext* ctx = GImGui)
2956         ctx->IO.MetricsActiveAllocations++;
2957     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2958 }
2959 
MemFree(void * ptr)2960 void ImGui::MemFree(void* ptr)
2961 {
2962     if (ptr)
2963         if (ImGuiContext* ctx = GImGui)
2964             ctx->IO.MetricsActiveAllocations--;
2965     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2966 }
2967 
GetClipboardText()2968 const char* ImGui::GetClipboardText()
2969 {
2970     return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2971 }
2972 
SetClipboardText(const char * text)2973 void ImGui::SetClipboardText(const char* text)
2974 {
2975     if (GImGui->IO.SetClipboardTextFn)
2976         GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2977 }
2978 
GetVersion()2979 const char* ImGui::GetVersion()
2980 {
2981     return IMGUI_VERSION;
2982 }
2983 
2984 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2985 // 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()2986 ImGuiContext* ImGui::GetCurrentContext()
2987 {
2988     return GImGui;
2989 }
2990 
SetCurrentContext(ImGuiContext * ctx)2991 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2992 {
2993 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2994     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2995 #else
2996     GImGui = ctx;
2997 #endif
2998 }
2999 
3000 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3001 // 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)3002 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)
3003 {
3004     bool error = false;
3005     if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!");  }
3006     if (sz_io    != sizeof(ImGuiIO))       { error = true; IM_ASSERT(sz_io    == sizeof(ImGuiIO)      && "Mismatched struct layout!"); }
3007     if (sz_style != sizeof(ImGuiStyle))    { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle)   && "Mismatched struct layout!"); }
3008     if (sz_vec2  != sizeof(ImVec2))        { error = true; IM_ASSERT(sz_vec2  == sizeof(ImVec2)       && "Mismatched struct layout!"); }
3009     if (sz_vec4  != sizeof(ImVec4))        { error = true; IM_ASSERT(sz_vec4  == sizeof(ImVec4)       && "Mismatched struct layout!"); }
3010     if (sz_vert  != sizeof(ImDrawVert))    { error = true; IM_ASSERT(sz_vert  == sizeof(ImDrawVert)   && "Mismatched struct layout!"); }
3011     return !error;
3012 }
3013 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3014 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3015 {
3016     GImAllocatorAllocFunc = alloc_func;
3017     GImAllocatorFreeFunc = free_func;
3018     GImAllocatorUserData = user_data;
3019 }
3020 
CreateContext(ImFontAtlas * shared_font_atlas)3021 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3022 {
3023     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3024     if (GImGui == NULL)
3025         SetCurrentContext(ctx);
3026     Initialize(ctx);
3027     return ctx;
3028 }
3029 
DestroyContext(ImGuiContext * ctx)3030 void ImGui::DestroyContext(ImGuiContext* ctx)
3031 {
3032     if (ctx == NULL)
3033         ctx = GImGui;
3034     Shutdown(ctx);
3035     if (GImGui == ctx)
3036         SetCurrentContext(NULL);
3037     IM_DELETE(ctx);
3038 }
3039 
GetIO()3040 ImGuiIO& ImGui::GetIO()
3041 {
3042     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3043     return GImGui->IO;
3044 }
3045 
GetStyle()3046 ImGuiStyle& ImGui::GetStyle()
3047 {
3048     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3049     return GImGui->Style;
3050 }
3051 
3052 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3053 ImDrawData* ImGui::GetDrawData()
3054 {
3055     ImGuiContext& g = *GImGui;
3056     return g.DrawData.Valid ? &g.DrawData : NULL;
3057 }
3058 
GetTime()3059 double ImGui::GetTime()
3060 {
3061     return GImGui->Time;
3062 }
3063 
GetFrameCount()3064 int ImGui::GetFrameCount()
3065 {
3066     return GImGui->FrameCount;
3067 }
3068 
GetOverlayDrawList(ImGuiWindow *)3069 static ImDrawList* GetOverlayDrawList(ImGuiWindow*)
3070 {
3071     // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches.
3072     return &GImGui->OverlayDrawList;
3073 }
3074 
GetOverlayDrawList()3075 ImDrawList* ImGui::GetOverlayDrawList()
3076 {
3077     return &GImGui->OverlayDrawList;
3078 }
3079 
GetDrawListSharedData()3080 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3081 {
3082     return &GImGui->DrawListSharedData;
3083 }
3084 
StartMouseMovingWindow(ImGuiWindow * window)3085 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3086 {
3087     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3088     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3089     // This is because we want ActiveId to be set even when the window is not permitted to move.
3090     ImGuiContext& g = *GImGui;
3091     FocusWindow(window);
3092     SetActiveID(window->MoveId, window);
3093     g.NavDisableHighlight = true;
3094     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3095 
3096     bool can_move_window = true;
3097     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3098         can_move_window = false;
3099     if (can_move_window)
3100         g.MovingWindow = window;
3101 }
3102 
3103 // Handle mouse moving window
3104 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3105 void ImGui::UpdateMouseMovingWindowNewFrame()
3106 {
3107     ImGuiContext& g = *GImGui;
3108     if (g.MovingWindow != NULL)
3109     {
3110         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3111         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3112         KeepAliveID(g.ActiveId);
3113         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3114         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3115         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3116         {
3117             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3118             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3119             {
3120                 MarkIniSettingsDirty(moving_window);
3121                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3122             }
3123             FocusWindow(g.MovingWindow);
3124         }
3125         else
3126         {
3127             ClearActiveID();
3128             g.MovingWindow = NULL;
3129         }
3130     }
3131     else
3132     {
3133         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3134         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3135         {
3136             KeepAliveID(g.ActiveId);
3137             if (!g.IO.MouseDown[0])
3138                 ClearActiveID();
3139         }
3140     }
3141 }
3142 
3143 // Initiate moving window, handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame()3144 void ImGui::UpdateMouseMovingWindowEndFrame()
3145 {
3146     // Initiate moving window
3147     ImGuiContext& g = *GImGui;
3148     if (g.ActiveId != 0 || g.HoveredId != 0)
3149         return;
3150 
3151     // Unless we just made a window/popup appear
3152     if (g.NavWindow && g.NavWindow->Appearing)
3153         return;
3154 
3155     // Click to focus window and start moving (after we're done with all our widgets)
3156     if (g.IO.MouseClicked[0])
3157     {
3158         if (g.HoveredRootWindow != NULL)
3159         {
3160             StartMouseMovingWindow(g.HoveredWindow);
3161             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3162                 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3163                     g.MovingWindow = NULL;
3164         }
3165         else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3166         {
3167             // Clicking on void disable focus
3168             FocusWindow(NULL);
3169         }
3170     }
3171 
3172     // With right mouse button we close popups without changing focus
3173     // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3174     if (g.IO.MouseClicked[1])
3175     {
3176         // Find the top-most window between HoveredWindow and the front most Modal Window.
3177         // This is where we can trim the popup stack.
3178         ImGuiWindow* modal = GetFrontMostPopupModal();
3179         bool hovered_window_above_modal = false;
3180         if (modal == NULL)
3181             hovered_window_above_modal = true;
3182         for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3183         {
3184             ImGuiWindow* window = g.Windows[i];
3185             if (window == modal)
3186                 break;
3187             if (window == g.HoveredWindow)
3188                 hovered_window_above_modal = true;
3189         }
3190         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
3191     }
3192 }
3193 
IsWindowActiveAndVisible(ImGuiWindow * window)3194 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3195 {
3196     return (window->Active) && (!window->Hidden);
3197 }
3198 
UpdateMouseInputs()3199 static void ImGui::UpdateMouseInputs()
3200 {
3201     ImGuiContext& g = *GImGui;
3202 
3203     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3204     if (IsMousePosValid(&g.IO.MousePos))
3205         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3206 
3207     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3208     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3209         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3210     else
3211         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3212     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3213         g.NavDisableMouseHover = false;
3214 
3215     g.IO.MousePosPrev = g.IO.MousePos;
3216     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3217     {
3218         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3219         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3220         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3221         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;
3222         g.IO.MouseDoubleClicked[i] = false;
3223         if (g.IO.MouseClicked[i])
3224         {
3225             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3226             {
3227                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3228                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3229                     g.IO.MouseDoubleClicked[i] = true;
3230                 g.IO.MouseClickedTime[i] = -FLT_MAX;    // so the third click isn't turned into a double-click
3231             }
3232             else
3233             {
3234                 g.IO.MouseClickedTime[i] = g.Time;
3235             }
3236             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3237             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3238             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3239         }
3240         else if (g.IO.MouseDown[i])
3241         {
3242             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3243             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3244             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3245             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);
3246             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);
3247         }
3248         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3249             g.NavDisableMouseHover = false;
3250     }
3251 }
3252 
UpdateMouseWheel()3253 void ImGui::UpdateMouseWheel()
3254 {
3255     ImGuiContext& g = *GImGui;
3256     if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
3257         return;
3258     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3259         return;
3260 
3261     // 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).
3262     ImGuiWindow* window = g.HoveredWindow;
3263     ImGuiWindow* scroll_window = window;
3264     while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs) && scroll_window->ParentWindow)
3265         scroll_window = scroll_window->ParentWindow;
3266     const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs);
3267 
3268     if (g.IO.MouseWheel != 0.0f)
3269     {
3270         if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3271         {
3272             // Zoom / Scale window
3273             const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3274             const float scale = new_font_scale / window->FontWindowScale;
3275             window->FontWindowScale = new_font_scale;
3276 
3277             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3278             window->Pos += offset;
3279             window->Size *= scale;
3280             window->SizeFull *= scale;
3281         }
3282         else if (!g.IO.KeyCtrl && scroll_allowed)
3283         {
3284             // Mouse wheel vertical scrolling
3285             float scroll_amount = 5 * scroll_window->CalcFontSize();
3286             scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3287             SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3288         }
3289     }
3290     if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3291     {
3292         // Mouse wheel horizontal scrolling (for hardware that supports it)
3293         float scroll_amount = scroll_window->CalcFontSize();
3294         SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3295     }
3296 }
3297 
3298 // 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()3299 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3300 {
3301     ImGuiContext& g = *GImGui;
3302 
3303     // Find the window hovered by mouse:
3304     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3305     // - 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.
3306     // - 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.
3307     FindHoveredWindow();
3308 
3309     // Modal windows prevents cursor from hovering behind them.
3310     ImGuiWindow* modal_window = GetFrontMostPopupModal();
3311     if (modal_window)
3312         if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3313             g.HoveredRootWindow = g.HoveredWindow = NULL;
3314 
3315     // Disabled mouse?
3316     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3317         g.HoveredWindow = g.HoveredRootWindow = NULL;
3318 
3319     // 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.
3320     int mouse_earliest_button_down = -1;
3321     bool mouse_any_down = false;
3322     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3323     {
3324         if (g.IO.MouseClicked[i])
3325             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3326         mouse_any_down |= g.IO.MouseDown[i];
3327         if (g.IO.MouseDown[i])
3328             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3329                 mouse_earliest_button_down = i;
3330     }
3331     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3332 
3333     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3334     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3335     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3336     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3337         g.HoveredWindow = g.HoveredRootWindow = NULL;
3338 
3339     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3340     if (g.WantCaptureMouseNextFrame != -1)
3341         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3342     else
3343         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3344 
3345     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3346     if (g.WantCaptureKeyboardNextFrame != -1)
3347         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3348     else
3349         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3350     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3351         g.IO.WantCaptureKeyboard = true;
3352 
3353     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3354     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3355 }
3356 
NewFrame()3357 void ImGui::NewFrame()
3358 {
3359     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3360     ImGuiContext& g = *GImGui;
3361 
3362 #ifdef IMGUI_ENABLE_TEST_ENGINE
3363     ImGuiTestEngineHook_PreNewFrame(&g);
3364 #endif
3365 
3366     // Check user data
3367     // (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)
3368     IM_ASSERT(g.Initialized);
3369     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
3370     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
3371     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3372     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3373     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
3374     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)!");
3375     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3376     for (int n = 0; n < ImGuiKey_COUNT; n++)
3377         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)");
3378 
3379     // 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)
3380     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3381         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3382 
3383     // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3384     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3385         g.IO.ConfigWindowsResizeFromEdges = false;
3386 
3387     // Load settings on first frame (if not explicitly loaded manually before)
3388     if (!g.SettingsLoaded)
3389     {
3390         IM_ASSERT(g.SettingsWindows.empty());
3391         if (g.IO.IniFilename)
3392             LoadIniSettingsFromDisk(g.IO.IniFilename);
3393         g.SettingsLoaded = true;
3394     }
3395 
3396     // Save settings (with a delay after the last modification, so we don't spam disk too much)
3397     if (g.SettingsDirtyTimer > 0.0f)
3398     {
3399         g.SettingsDirtyTimer -= g.IO.DeltaTime;
3400         if (g.SettingsDirtyTimer <= 0.0f)
3401         {
3402             if (g.IO.IniFilename != NULL)
3403                 SaveIniSettingsToDisk(g.IO.IniFilename);
3404             else
3405                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3406             g.SettingsDirtyTimer = 0.0f;
3407         }
3408     }
3409 
3410     g.Time += g.IO.DeltaTime;
3411     g.FrameScopeActive = true;
3412     g.FrameCount += 1;
3413     g.TooltipOverrideCount = 0;
3414     g.WindowsActiveCount = 0;
3415 
3416     // Setup current font and draw list shared data
3417     g.IO.Fonts->Locked = true;
3418     SetCurrentFont(GetDefaultFont());
3419     IM_ASSERT(g.Font->IsLoaded());
3420     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3421     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3422 
3423     g.OverlayDrawList.Clear();
3424     g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3425     g.OverlayDrawList.PushClipRectFullScreen();
3426     g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3427 
3428     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3429     g.DrawData.Clear();
3430 
3431     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3432     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3433         KeepAliveID(g.DragDropPayload.SourceId);
3434 
3435     // Clear reference to active widget if the widget isn't alive anymore
3436     if (!g.HoveredIdPreviousFrame)
3437         g.HoveredIdTimer = 0.0f;
3438     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3439         g.HoveredIdNotActiveTimer = 0.0f;
3440     if (g.HoveredId)
3441         g.HoveredIdTimer += g.IO.DeltaTime;
3442     if (g.HoveredId && g.ActiveId != g.HoveredId)
3443         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3444     g.HoveredIdPreviousFrame = g.HoveredId;
3445     g.HoveredId = 0;
3446     g.HoveredIdAllowOverlap = false;
3447     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3448         ClearActiveID();
3449     if (g.ActiveId)
3450         g.ActiveIdTimer += g.IO.DeltaTime;
3451     g.LastActiveIdTimer += g.IO.DeltaTime;
3452     g.ActiveIdPreviousFrame = g.ActiveId;
3453     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3454     g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3455     g.ActiveIdIsAlive = 0;
3456     g.ActiveIdPreviousFrameIsAlive = false;
3457     g.ActiveIdIsJustActivated = false;
3458     if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3459         g.ScalarAsInputTextId = 0;
3460 
3461     // Drag and drop
3462     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3463     g.DragDropAcceptIdCurr = 0;
3464     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3465     g.DragDropWithinSourceOrTarget = false;
3466 
3467     // Update keyboard input state
3468     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3469     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3470         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;
3471 
3472     // Update gamepad/keyboard directional navigation
3473     NavUpdate();
3474 
3475     // Update mouse input state
3476     UpdateMouseInputs();
3477 
3478     // Calculate frame-rate for the user, as a purely luxurious feature
3479     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3480     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3481     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3482     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3483 
3484     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3485     UpdateMouseMovingWindowNewFrame();
3486     UpdateHoveredWindowAndCaptureFlags();
3487 
3488     // Background darkening/whitening
3489     if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3490         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3491     else
3492         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3493 
3494     g.MouseCursor = ImGuiMouseCursor_Arrow;
3495     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3496     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3497 
3498     // Mouse wheel scrolling, scale
3499     UpdateMouseWheel();
3500 
3501     // Pressing TAB activate widget focus
3502     if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3503     {
3504         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3505             g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3506         else
3507             g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3508     }
3509     g.NavIdTabCounter = INT_MAX;
3510 
3511     // Mark all windows as not visible
3512     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3513     for (int i = 0; i != g.Windows.Size; i++)
3514     {
3515         ImGuiWindow* window = g.Windows[i];
3516         window->WasActive = window->Active;
3517         window->Active = false;
3518         window->WriteAccessed = false;
3519     }
3520 
3521     // Closing the focused window restore focus to the first active root window in descending z-order
3522     if (g.NavWindow && !g.NavWindow->WasActive)
3523         FocusPreviousWindowIgnoringOne(NULL);
3524 
3525     // No window should be open at the beginning of the frame.
3526     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3527     g.CurrentWindowStack.resize(0);
3528     g.BeginPopupStack.resize(0);
3529     ClosePopupsOverWindow(g.NavWindow);
3530 
3531     // Create implicit/fallback window - which we will only render it if the user has added something to it.
3532     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3533     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3534     SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3535     Begin("Debug##Default");
3536     g.FrameScopePushedImplicitWindow = true;
3537 
3538 #ifdef IMGUI_ENABLE_TEST_ENGINE
3539     ImGuiTestEngineHook_PostNewFrame(&g);
3540 #endif
3541 }
3542 
Initialize(ImGuiContext * context)3543 void ImGui::Initialize(ImGuiContext* context)
3544 {
3545     ImGuiContext& g = *context;
3546     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3547 
3548     // Add .ini handle for ImGuiWindow type
3549     ImGuiSettingsHandler ini_handler;
3550     ini_handler.TypeName = "Window";
3551     ini_handler.TypeHash = ImHashStr("Window", 0);
3552     ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3553     ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3554     ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3555     g.SettingsHandlers.push_back(ini_handler);
3556 
3557     g.Initialized = true;
3558 }
3559 
3560 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3561 void ImGui::Shutdown(ImGuiContext* context)
3562 {
3563     // 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)
3564     ImGuiContext& g = *context;
3565     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3566     {
3567         g.IO.Fonts->Locked = false;
3568         IM_DELETE(g.IO.Fonts);
3569     }
3570     g.IO.Fonts = NULL;
3571 
3572     // Cleanup of other data are conditional on actually having initialized ImGui.
3573     if (!g.Initialized)
3574         return;
3575 
3576     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3577     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3578     {
3579         ImGuiContext* backup_context = GImGui;
3580         SetCurrentContext(context);
3581         SaveIniSettingsToDisk(g.IO.IniFilename);
3582         SetCurrentContext(backup_context);
3583     }
3584 
3585     // Clear everything else
3586     for (int i = 0; i < g.Windows.Size; i++)
3587         IM_DELETE(g.Windows[i]);
3588     g.Windows.clear();
3589     g.WindowsFocusOrder.clear();
3590     g.WindowsSortBuffer.clear();
3591     g.CurrentWindow = NULL;
3592     g.CurrentWindowStack.clear();
3593     g.WindowsById.Clear();
3594     g.NavWindow = NULL;
3595     g.HoveredWindow = g.HoveredRootWindow = NULL;
3596     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3597     g.MovingWindow = NULL;
3598     g.ColorModifiers.clear();
3599     g.StyleModifiers.clear();
3600     g.FontStack.clear();
3601     g.OpenPopupStack.clear();
3602     g.BeginPopupStack.clear();
3603     g.DrawDataBuilder.ClearFreeMemory();
3604     g.OverlayDrawList.ClearFreeMemory();
3605     g.PrivateClipboard.clear();
3606     g.InputTextState.TextW.clear();
3607     g.InputTextState.InitialText.clear();
3608     g.InputTextState.TempBuffer.clear();
3609 
3610     for (int i = 0; i < g.SettingsWindows.Size; i++)
3611         IM_DELETE(g.SettingsWindows[i].Name);
3612     g.SettingsWindows.clear();
3613     g.SettingsHandlers.clear();
3614 
3615     if (g.LogFile && g.LogFile != stdout)
3616     {
3617         fclose(g.LogFile);
3618         g.LogFile = NULL;
3619     }
3620     g.LogClipboard.clear();
3621 
3622     g.Initialized = false;
3623 }
3624 
3625 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3626 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3627 {
3628     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3629     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3630     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3631         return d;
3632     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3633         return d;
3634     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3635 }
3636 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3637 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3638 {
3639     out_sorted_windows->push_back(window);
3640     if (window->Active)
3641     {
3642         int count = window->DC.ChildWindows.Size;
3643         if (count > 1)
3644             ImQsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3645         for (int i = 0; i < count; i++)
3646         {
3647             ImGuiWindow* child = window->DC.ChildWindows[i];
3648             if (child->Active)
3649                 AddWindowToSortBuffer(out_sorted_windows, child);
3650         }
3651     }
3652 }
3653 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3654 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3655 {
3656     if (draw_list->CmdBuffer.empty())
3657         return;
3658 
3659     // Remove trailing command if unused
3660     ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3661     if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3662     {
3663         draw_list->CmdBuffer.pop_back();
3664         if (draw_list->CmdBuffer.empty())
3665             return;
3666     }
3667 
3668     // 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.
3669     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3670     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3671     IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3672 
3673     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3674     // If this assert triggers because you are drawing lots of stuff manually:
3675     // 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.
3676     // 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.
3677     //    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:
3678     //      glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3679     //    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.
3680     // 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.
3681     if (sizeof(ImDrawIdx) == 2)
3682         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3683 
3684     out_list->push_back(draw_list);
3685 }
3686 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3687 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3688 {
3689     ImGuiContext& g = *GImGui;
3690     g.IO.MetricsRenderWindows++;
3691     AddDrawListToDrawData(out_render_list, window->DrawList);
3692     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3693     {
3694         ImGuiWindow* child = window->DC.ChildWindows[i];
3695         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3696             AddWindowToDrawData(out_render_list, child);
3697     }
3698 }
3699 
AddRootWindowToDrawData(ImGuiWindow * window)3700 static void AddRootWindowToDrawData(ImGuiWindow* window)
3701 {
3702     ImGuiContext& g = *GImGui;
3703     if (window->Flags & ImGuiWindowFlags_Tooltip)
3704         AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3705     else
3706         AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3707 }
3708 
FlattenIntoSingleLayer()3709 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3710 {
3711     int n = Layers[0].Size;
3712     int size = n;
3713     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3714         size += Layers[i].Size;
3715     Layers[0].resize(size);
3716     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3717     {
3718         ImVector<ImDrawList*>& layer = Layers[layer_n];
3719         if (layer.empty())
3720             continue;
3721         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3722         n += layer.Size;
3723         layer.resize(0);
3724     }
3725 }
3726 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3727 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3728 {
3729     ImGuiIO& io = ImGui::GetIO();
3730     draw_data->Valid = true;
3731     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3732     draw_data->CmdListsCount = draw_lists->Size;
3733     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3734     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3735     draw_data->DisplaySize = io.DisplaySize;
3736     draw_data->FramebufferScale = io.DisplayFramebufferScale;
3737     for (int n = 0; n < draw_lists->Size; n++)
3738     {
3739         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3740         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3741     }
3742 }
3743 
3744 // 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)3745 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3746 {
3747     ImGuiWindow* window = GetCurrentWindow();
3748     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3749     window->ClipRect = window->DrawList->_ClipRectStack.back();
3750 }
3751 
PopClipRect()3752 void ImGui::PopClipRect()
3753 {
3754     ImGuiWindow* window = GetCurrentWindow();
3755     window->DrawList->PopClipRect();
3756     window->ClipRect = window->DrawList->_ClipRectStack.back();
3757 }
3758 
3759 // 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()3760 void ImGui::EndFrame()
3761 {
3762     ImGuiContext& g = *GImGui;
3763     IM_ASSERT(g.Initialized);
3764     if (g.FrameCountEnded == g.FrameCount)          // Don't process EndFrame() multiple times.
3765         return;
3766     IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?");
3767 
3768     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3769     if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
3770     {
3771         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3772         g.PlatformImeLastPos = g.PlatformImePos;
3773     }
3774 
3775     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
3776     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
3777     if (g.CurrentWindowStack.Size != 1)
3778     {
3779         if (g.CurrentWindowStack.Size > 1)
3780         {
3781             IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
3782             while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING
3783                 End();
3784         }
3785         else
3786         {
3787             IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
3788         }
3789     }
3790 
3791     // Hide implicit/fallback "Debug" window if it hasn't been used
3792     g.FrameScopePushedImplicitWindow = false;
3793     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3794         g.CurrentWindow->Active = false;
3795     End();
3796 
3797     // Show CTRL+TAB list window
3798     if (g.NavWindowingTarget)
3799         NavUpdateWindowingList();
3800 
3801     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3802     if (g.DragDropActive)
3803     {
3804         bool is_delivered = g.DragDropPayload.Delivery;
3805         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
3806         if (is_delivered || is_elapsed)
3807             ClearDragDrop();
3808     }
3809 
3810     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3811     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3812     {
3813         g.DragDropWithinSourceOrTarget = true;
3814         SetTooltip("...");
3815         g.DragDropWithinSourceOrTarget = false;
3816     }
3817 
3818     // End frame
3819     g.FrameScopeActive = false;
3820     g.FrameCountEnded = g.FrameCount;
3821 
3822     // Initiate moving window + handle left-click and right-click focus
3823     UpdateMouseMovingWindowEndFrame();
3824 
3825     // Sort the window list so that all child windows are after their parent
3826     // We cannot do that on FocusWindow() because childs may not exist yet
3827     g.WindowsSortBuffer.resize(0);
3828     g.WindowsSortBuffer.reserve(g.Windows.Size);
3829     for (int i = 0; i != g.Windows.Size; i++)
3830     {
3831         ImGuiWindow* window = g.Windows[i];
3832         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
3833             continue;
3834         AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
3835     }
3836 
3837     // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.
3838     IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);
3839     g.Windows.swap(g.WindowsSortBuffer);
3840     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3841 
3842     // Unlock font atlas
3843     g.IO.Fonts->Locked = false;
3844 
3845     // Clear Input data for next frame
3846     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3847     g.IO.InputQueueCharacters.resize(0);
3848     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
3849 }
3850 
Render()3851 void ImGui::Render()
3852 {
3853     ImGuiContext& g = *GImGui;
3854     IM_ASSERT(g.Initialized);
3855 
3856     if (g.FrameCountEnded != g.FrameCount)
3857         EndFrame();
3858     g.FrameCountRendered = g.FrameCount;
3859 
3860     // Gather ImDrawList to render (for each active window)
3861     g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3862     g.DrawDataBuilder.Clear();
3863     ImGuiWindow* windows_to_render_front_most[2];
3864     windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3865     windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3866     for (int n = 0; n != g.Windows.Size; n++)
3867     {
3868         ImGuiWindow* window = g.Windows[n];
3869         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3870             AddRootWindowToDrawData(window);
3871     }
3872     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3873         if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3874             AddRootWindowToDrawData(windows_to_render_front_most[n]);
3875     g.DrawDataBuilder.FlattenIntoSingleLayer();
3876 
3877     // Draw software mouse cursor if requested
3878     if (g.IO.MouseDrawCursor)
3879         RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor);
3880 
3881     if (!g.OverlayDrawList.VtxBuffer.empty())
3882         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
3883 
3884     // Setup ImDrawData structure for end-user
3885     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
3886     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3887     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3888 
3889     // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
3890 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3891     if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3892         g.IO.RenderDrawListsFn(&g.DrawData);
3893 #endif
3894 }
3895 
3896 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3897 // 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)3898 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3899 {
3900     ImGuiContext& g = *GImGui;
3901 
3902     const char* text_display_end;
3903     if (hide_text_after_double_hash)
3904         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
3905     else
3906         text_display_end = text_end;
3907 
3908     ImFont* font = g.Font;
3909     const float font_size = g.FontSize;
3910     if (text == text_display_end)
3911         return ImVec2(0.0f, font_size);
3912     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3913 
3914     // Round
3915     text_size.x = (float)(int)(text_size.x + 0.95f);
3916 
3917     return text_size;
3918 }
3919 
3920 // Helper to calculate coarse clipping of large list of evenly sized items.
3921 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3922 // 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)3923 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3924 {
3925     ImGuiContext& g = *GImGui;
3926     ImGuiWindow* window = g.CurrentWindow;
3927     if (g.LogEnabled)
3928     {
3929         // If logging is active, do not perform any clipping
3930         *out_items_display_start = 0;
3931         *out_items_display_end = items_count;
3932         return;
3933     }
3934     if (window->SkipItems)
3935     {
3936         *out_items_display_start = *out_items_display_end = 0;
3937         return;
3938     }
3939 
3940     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3941     ImRect unclipped_rect = window->ClipRect;
3942     if (g.NavMoveRequest)
3943         unclipped_rect.Add(g.NavScoringRectScreen);
3944 
3945     const ImVec2 pos = window->DC.CursorPos;
3946     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3947     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3948 
3949     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
3950     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3951         start--;
3952     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3953         end++;
3954 
3955     start = ImClamp(start, 0, items_count);
3956     end = ImClamp(end + 1, start, items_count);
3957     *out_items_display_start = start;
3958     *out_items_display_end = end;
3959 }
3960 
3961 // Find window given position, search front-to-back
3962 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3963 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3964 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()3965 static void FindHoveredWindow()
3966 {
3967     ImGuiContext& g = *GImGui;
3968 
3969     ImGuiWindow* hovered_window = NULL;
3970     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
3971         hovered_window = g.MovingWindow;
3972 
3973     ImVec2 padding_regular = g.Style.TouchExtraPadding;
3974     ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
3975     for (int i = g.Windows.Size - 1; i >= 0; i--)
3976     {
3977         ImGuiWindow* window = g.Windows[i];
3978         if (!window->Active || window->Hidden)
3979             continue;
3980         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
3981             continue;
3982 
3983         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3984         ImRect bb(window->OuterRectClipped);
3985         if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize))
3986             bb.Expand(padding_regular);
3987         else
3988             bb.Expand(padding_for_resize_from_edges);
3989         if (!bb.Contains(g.IO.MousePos))
3990             continue;
3991 
3992         // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
3993         if (hovered_window == NULL)
3994             hovered_window = window;
3995         if (hovered_window)
3996             break;
3997     }
3998 
3999     g.HoveredWindow = hovered_window;
4000     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4001 
4002 }
4003 
4004 // Test if mouse cursor is hovering given rectangle
4005 // NB- Rectangle is clipped by our current clip setting
4006 // 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)4007 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4008 {
4009     ImGuiContext& g = *GImGui;
4010 
4011     // Clip
4012     ImRect rect_clipped(r_min, r_max);
4013     if (clip)
4014         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4015 
4016     // Expand for touch input
4017     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4018     if (!rect_for_touch.Contains(g.IO.MousePos))
4019         return false;
4020     return true;
4021 }
4022 
GetKeyIndex(ImGuiKey imgui_key)4023 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4024 {
4025     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4026     return GImGui->IO.KeyMap[imgui_key];
4027 }
4028 
4029 // 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)4030 bool ImGui::IsKeyDown(int user_key_index)
4031 {
4032     if (user_key_index < 0) return false;
4033     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
4034     return GImGui->IO.KeysDown[user_key_index];
4035 }
4036 
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)4037 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
4038 {
4039     if (t == 0.0f)
4040         return 1;
4041     if (t <= repeat_delay || repeat_rate <= 0.0f)
4042         return 0;
4043     const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
4044     return (count > 0) ? count : 0;
4045 }
4046 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4047 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4048 {
4049     ImGuiContext& g = *GImGui;
4050     if (key_index < 0)
4051         return 0;
4052     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4053     const float t = g.IO.KeysDownDuration[key_index];
4054     return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
4055 }
4056 
IsKeyPressed(int user_key_index,bool repeat)4057 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4058 {
4059     ImGuiContext& g = *GImGui;
4060     if (user_key_index < 0)
4061         return false;
4062     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4063     const float t = g.IO.KeysDownDuration[user_key_index];
4064     if (t == 0.0f)
4065         return true;
4066     if (repeat && t > g.IO.KeyRepeatDelay)
4067         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4068     return false;
4069 }
4070 
IsKeyReleased(int user_key_index)4071 bool ImGui::IsKeyReleased(int user_key_index)
4072 {
4073     ImGuiContext& g = *GImGui;
4074     if (user_key_index < 0) return false;
4075     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4076     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4077 }
4078 
IsMouseDown(int button)4079 bool ImGui::IsMouseDown(int button)
4080 {
4081     ImGuiContext& g = *GImGui;
4082     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4083     return g.IO.MouseDown[button];
4084 }
4085 
IsAnyMouseDown()4086 bool ImGui::IsAnyMouseDown()
4087 {
4088     ImGuiContext& g = *GImGui;
4089     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4090         if (g.IO.MouseDown[n])
4091             return true;
4092     return false;
4093 }
4094 
IsMouseClicked(int button,bool repeat)4095 bool ImGui::IsMouseClicked(int button, bool repeat)
4096 {
4097     ImGuiContext& g = *GImGui;
4098     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4099     const float t = g.IO.MouseDownDuration[button];
4100     if (t == 0.0f)
4101         return true;
4102 
4103     if (repeat && t > g.IO.KeyRepeatDelay)
4104     {
4105         float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
4106         if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
4107             return true;
4108     }
4109 
4110     return false;
4111 }
4112 
IsMouseReleased(int button)4113 bool ImGui::IsMouseReleased(int button)
4114 {
4115     ImGuiContext& g = *GImGui;
4116     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4117     return g.IO.MouseReleased[button];
4118 }
4119 
IsMouseDoubleClicked(int button)4120 bool ImGui::IsMouseDoubleClicked(int button)
4121 {
4122     ImGuiContext& g = *GImGui;
4123     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4124     return g.IO.MouseDoubleClicked[button];
4125 }
4126 
IsMouseDragging(int button,float lock_threshold)4127 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4128 {
4129     ImGuiContext& g = *GImGui;
4130     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4131     if (!g.IO.MouseDown[button])
4132         return false;
4133     if (lock_threshold < 0.0f)
4134         lock_threshold = g.IO.MouseDragThreshold;
4135     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4136 }
4137 
GetMousePos()4138 ImVec2 ImGui::GetMousePos()
4139 {
4140     return GImGui->IO.MousePos;
4141 }
4142 
4143 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4144 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4145 {
4146     ImGuiContext& g = *GImGui;
4147     if (g.BeginPopupStack.Size > 0)
4148         return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4149     return g.IO.MousePos;
4150 }
4151 
4152 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4153 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4154 {
4155     // The assert is only to silence a false-positive in XCode Static Analysis.
4156     // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).
4157     IM_ASSERT(GImGui != NULL);
4158     const float MOUSE_INVALID = -256000.0f;
4159     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4160     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4161 }
4162 
4163 // Return the delta from the initial clicking position.
4164 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4165 // 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)4166 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4167 {
4168     ImGuiContext& g = *GImGui;
4169     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4170     if (lock_threshold < 0.0f)
4171         lock_threshold = g.IO.MouseDragThreshold;
4172     if (g.IO.MouseDown[button])
4173         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4174             return g.IO.MousePos - g.IO.MouseClickedPos[button];     // Assume we can only get active with left-mouse button (at the moment).
4175     return ImVec2(0.0f, 0.0f);
4176 }
4177 
ResetMouseDragDelta(int button)4178 void ImGui::ResetMouseDragDelta(int button)
4179 {
4180     ImGuiContext& g = *GImGui;
4181     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4182     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4183     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4184 }
4185 
GetMouseCursor()4186 ImGuiMouseCursor ImGui::GetMouseCursor()
4187 {
4188     return GImGui->MouseCursor;
4189 }
4190 
SetMouseCursor(ImGuiMouseCursor cursor_type)4191 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4192 {
4193     GImGui->MouseCursor = cursor_type;
4194 }
4195 
CaptureKeyboardFromApp(bool capture)4196 void ImGui::CaptureKeyboardFromApp(bool capture)
4197 {
4198     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4199 }
4200 
CaptureMouseFromApp(bool capture)4201 void ImGui::CaptureMouseFromApp(bool capture)
4202 {
4203     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4204 }
4205 
IsItemActive()4206 bool ImGui::IsItemActive()
4207 {
4208     ImGuiContext& g = *GImGui;
4209     if (g.ActiveId)
4210     {
4211         ImGuiWindow* window = g.CurrentWindow;
4212         return g.ActiveId == window->DC.LastItemId;
4213     }
4214     return false;
4215 }
4216 
IsItemActivated()4217 bool ImGui::IsItemActivated()
4218 {
4219     ImGuiContext& g = *GImGui;
4220     if (g.ActiveId)
4221     {
4222         ImGuiWindow* window = g.CurrentWindow;
4223         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4224             return true;
4225     }
4226     return false;
4227 }
4228 
IsItemDeactivated()4229 bool ImGui::IsItemDeactivated()
4230 {
4231     ImGuiContext& g = *GImGui;
4232     ImGuiWindow* window = g.CurrentWindow;
4233     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4234 }
4235 
IsItemDeactivatedAfterEdit()4236 bool ImGui::IsItemDeactivatedAfterEdit()
4237 {
4238     ImGuiContext& g = *GImGui;
4239     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
4240 }
4241 
IsItemFocused()4242 bool ImGui::IsItemFocused()
4243 {
4244     ImGuiContext& g = *GImGui;
4245     ImGuiWindow* window = g.CurrentWindow;
4246 
4247     if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4248         return false;
4249     return true;
4250 }
4251 
IsItemClicked(int mouse_button)4252 bool ImGui::IsItemClicked(int mouse_button)
4253 {
4254     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4255 }
4256 
IsAnyItemHovered()4257 bool ImGui::IsAnyItemHovered()
4258 {
4259     ImGuiContext& g = *GImGui;
4260     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4261 }
4262 
IsAnyItemActive()4263 bool ImGui::IsAnyItemActive()
4264 {
4265     ImGuiContext& g = *GImGui;
4266     return g.ActiveId != 0;
4267 }
4268 
IsAnyItemFocused()4269 bool ImGui::IsAnyItemFocused()
4270 {
4271     ImGuiContext& g = *GImGui;
4272     return g.NavId != 0 && !g.NavDisableHighlight;
4273 }
4274 
IsItemVisible()4275 bool ImGui::IsItemVisible()
4276 {
4277     ImGuiWindow* window = GetCurrentWindowRead();
4278     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4279 }
4280 
IsItemEdited()4281 bool ImGui::IsItemEdited()
4282 {
4283     ImGuiWindow* window = GetCurrentWindowRead();
4284     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4285 }
4286 
4287 // 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()4288 void ImGui::SetItemAllowOverlap()
4289 {
4290     ImGuiContext& g = *GImGui;
4291     if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4292         g.HoveredIdAllowOverlap = true;
4293     if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4294         g.ActiveIdAllowOverlap = true;
4295 }
4296 
GetItemRectMin()4297 ImVec2 ImGui::GetItemRectMin()
4298 {
4299     ImGuiWindow* window = GetCurrentWindowRead();
4300     return window->DC.LastItemRect.Min;
4301 }
4302 
GetItemRectMax()4303 ImVec2 ImGui::GetItemRectMax()
4304 {
4305     ImGuiWindow* window = GetCurrentWindowRead();
4306     return window->DC.LastItemRect.Max;
4307 }
4308 
GetItemRectSize()4309 ImVec2 ImGui::GetItemRectSize()
4310 {
4311     ImGuiWindow* window = GetCurrentWindowRead();
4312     return window->DC.LastItemRect.GetSize();
4313 }
4314 
GetViewportRect()4315 static ImRect GetViewportRect()
4316 {
4317     ImGuiContext& g = *GImGui;
4318     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4319 }
4320 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4321 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4322 {
4323     ImGuiContext& g = *GImGui;
4324     ImGuiWindow* parent_window = g.CurrentWindow;
4325 
4326     flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4327     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4328 
4329     // Size
4330     const ImVec2 content_avail = GetContentRegionAvail();
4331     ImVec2 size = ImFloor(size_arg);
4332     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4333     if (size.x <= 0.0f)
4334         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4335     if (size.y <= 0.0f)
4336         size.y = ImMax(content_avail.y + size.y, 4.0f);
4337     SetNextWindowSize(size);
4338 
4339     // 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.
4340     char title[256];
4341     if (name)
4342         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4343     else
4344         ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4345 
4346     const float backup_border_size = g.Style.ChildBorderSize;
4347     if (!border)
4348         g.Style.ChildBorderSize = 0.0f;
4349     bool ret = Begin(title, NULL, flags);
4350     g.Style.ChildBorderSize = backup_border_size;
4351 
4352     ImGuiWindow* child_window = g.CurrentWindow;
4353     child_window->ChildId = id;
4354     child_window->AutoFitChildAxises = auto_fit_axises;
4355 
4356     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4357     // While this is not really documented/defined, it seems that the expected thing to do.
4358     if (child_window->BeginCount == 1)
4359         parent_window->DC.CursorPos = child_window->Pos;
4360 
4361     // Process navigation-in immediately so NavInit can run on first frame
4362     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4363     {
4364         FocusWindow(child_window);
4365         NavInitWindow(child_window, false);
4366         SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4367         g.ActiveIdSource = ImGuiInputSource_Nav;
4368     }
4369     return ret;
4370 }
4371 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4372 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4373 {
4374     ImGuiWindow* window = GetCurrentWindow();
4375     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4376 }
4377 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4378 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4379 {
4380     IM_ASSERT(id != 0);
4381     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4382 }
4383 
EndChild()4384 void ImGui::EndChild()
4385 {
4386     ImGuiContext& g = *GImGui;
4387     ImGuiWindow* window = g.CurrentWindow;
4388 
4389     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() callss
4390     if (window->BeginCount > 1)
4391     {
4392         End();
4393     }
4394     else
4395     {
4396         ImVec2 sz = window->Size;
4397         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4398             sz.x = ImMax(4.0f, sz.x);
4399         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4400             sz.y = ImMax(4.0f, sz.y);
4401         End();
4402 
4403         ImGuiWindow* parent_window = g.CurrentWindow;
4404         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4405         ItemSize(sz);
4406         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4407         {
4408             ItemAdd(bb, window->ChildId);
4409             RenderNavHighlight(bb, window->ChildId);
4410 
4411             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4412             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4413                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4414         }
4415         else
4416         {
4417             // Not navigable into
4418             ItemAdd(bb, 0);
4419         }
4420     }
4421 }
4422 
4423 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4424 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4425 {
4426     ImGuiContext& g = *GImGui;
4427     const ImGuiStyle& style = g.Style;
4428     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4429     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4430     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4431     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4432     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4433     PopStyleVar(3);
4434     PopStyleColor();
4435     return ret;
4436 }
4437 
EndChildFrame()4438 void ImGui::EndChildFrame()
4439 {
4440     EndChild();
4441 }
4442 
4443 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4444 static void CheckStacksSize(ImGuiWindow* window, bool write)
4445 {
4446     // 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)
4447     ImGuiContext& g = *GImGui;
4448     short* p_backup = &window->DC.StackSizesBackup[0];
4449     { int current = window->IDStack.Size;       if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!");   p_backup++; }    // Too few or too many PopID()/TreePop()
4450     { int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!");                p_backup++; }    // Too few or too many EndGroup()
4451     { int current = g.BeginPopupStack.Size;     if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4452     // 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.
4453     { int current = g.ColorModifiers.Size;      if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!");       p_backup++; }    // Too few or too many PopStyleColor()
4454     { int current = g.StyleModifiers.Size;      if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!");           p_backup++; }    // Too few or too many PopStyleVar()
4455     { int current = g.FontStack.Size;           if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!");                   p_backup++; }    // Too few or too many PopFont()
4456     IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4457 }
4458 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4459 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4460 {
4461     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4462     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4463     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4464 }
4465 
FindWindowByID(ImGuiID id)4466 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4467 {
4468     ImGuiContext& g = *GImGui;
4469     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4470 }
4471 
FindWindowByName(const char * name)4472 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4473 {
4474     ImGuiID id = ImHashStr(name, 0);
4475     return FindWindowByID(id);
4476 }
4477 
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4478 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4479 {
4480     ImGuiContext& g = *GImGui;
4481 
4482     // Create window the first time
4483     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4484     window->Flags = flags;
4485     g.WindowsById.SetVoidPtr(window->ID, window);
4486 
4487     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4488     window->Pos = ImVec2(60, 60);
4489 
4490     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4491     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4492         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4493         {
4494             // Retrieve settings from .ini file
4495             window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
4496             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4497             window->Pos = ImFloor(settings->Pos);
4498             window->Collapsed = settings->Collapsed;
4499             if (ImLengthSqr(settings->Size) > 0.00001f)
4500                 size = ImFloor(settings->Size);
4501         }
4502     window->Size = window->SizeFull = window->SizeFullAtLastBegin = ImFloor(size);
4503     window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4504 
4505     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4506     {
4507         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4508         window->AutoFitOnlyGrows = false;
4509     }
4510     else
4511     {
4512         if (window->Size.x <= 0.0f)
4513             window->AutoFitFramesX = 2;
4514         if (window->Size.y <= 0.0f)
4515             window->AutoFitFramesY = 2;
4516         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4517     }
4518 
4519     g.WindowsFocusOrder.push_back(window);
4520     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4521         g.Windows.push_front(window); // Quite slow but rare and only once
4522     else
4523         g.Windows.push_back(window);
4524     return window;
4525 }
4526 
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4527 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4528 {
4529     ImGuiContext& g = *GImGui;
4530     if (g.NextWindowData.SizeConstraintCond != 0)
4531     {
4532         // Using -1,-1 on either X/Y axis to preserve the current size.
4533         ImRect cr = g.NextWindowData.SizeConstraintRect;
4534         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4535         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4536         if (g.NextWindowData.SizeCallback)
4537         {
4538             ImGuiSizeCallbackData data;
4539             data.UserData = g.NextWindowData.SizeCallbackUserData;
4540             data.Pos = window->Pos;
4541             data.CurrentSize = window->SizeFull;
4542             data.DesiredSize = new_size;
4543             g.NextWindowData.SizeCallback(&data);
4544             new_size = data.DesiredSize;
4545         }
4546     }
4547 
4548     // Minimum size
4549     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4550     {
4551         new_size = ImMax(new_size, g.Style.WindowMinSize);
4552         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
4553     }
4554     return new_size;
4555 }
4556 
CalcSizeContents(ImGuiWindow * window)4557 static ImVec2 CalcSizeContents(ImGuiWindow* window)
4558 {
4559     if (window->Collapsed)
4560         if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4561             return window->SizeContents;
4562     if (window->Hidden && window->HiddenFramesForResize == 0 && window->HiddenFramesRegular > 0)
4563         return window->SizeContents;
4564 
4565     ImVec2 sz;
4566     sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4567     sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4568     return sz + window->WindowPadding;
4569 }
4570 
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)4571 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4572 {
4573     ImGuiContext& g = *GImGui;
4574     ImGuiStyle& style = g.Style;
4575     if (window->Flags & ImGuiWindowFlags_Tooltip)
4576     {
4577         // Tooltip always resize
4578         return size_contents;
4579     }
4580     else
4581     {
4582         // Maximum window size is determined by the display size
4583         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4584         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4585         ImVec2 size_min = style.WindowMinSize;
4586         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)
4587             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4588         ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4589 
4590         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4591         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4592         ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
4593         if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4594             size_auto_fit.y += style.ScrollbarSize;
4595         if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4596             size_auto_fit.x += style.ScrollbarSize;
4597         return size_auto_fit;
4598     }
4599 }
4600 
CalcWindowExpectedSize(ImGuiWindow * window)4601 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4602 {
4603     ImVec2 size_contents = CalcSizeContents(window);
4604     return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents));
4605 }
4606 
GetWindowScrollMaxX(ImGuiWindow * window)4607 float ImGui::GetWindowScrollMaxX(ImGuiWindow* window)
4608 {
4609     return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4610 }
4611 
GetWindowScrollMaxY(ImGuiWindow * window)4612 float ImGui::GetWindowScrollMaxY(ImGuiWindow* window)
4613 {
4614     return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4615 }
4616 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)4617 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4618 {
4619     ImGuiContext& g = *GImGui;
4620     ImVec2 scroll = window->Scroll;
4621     if (window->ScrollTarget.x < FLT_MAX)
4622     {
4623         float cr_x = window->ScrollTargetCenterRatio.x;
4624         scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4625     }
4626     if (window->ScrollTarget.y < FLT_MAX)
4627     {
4628         // '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.
4629         float cr_y = window->ScrollTargetCenterRatio.y;
4630         float target_y = window->ScrollTarget.y;
4631         if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4632             target_y = 0.0f;
4633         if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4634             target_y = window->SizeContents.y;
4635         scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4636     }
4637     scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4638     if (!window->Collapsed && !window->SkipItems)
4639     {
4640         scroll.x = ImMin(scroll.x, ImGui::GetWindowScrollMaxX(window));
4641         scroll.y = ImMin(scroll.y, ImGui::GetWindowScrollMaxY(window));
4642     }
4643     return scroll;
4644 }
4645 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4646 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4647 {
4648     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4649         return ImGuiCol_PopupBg;
4650     if (flags & ImGuiWindowFlags_ChildWindow)
4651         return ImGuiCol_ChildBg;
4652     return ImGuiCol_WindowBg;
4653 }
4654 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4655 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4656 {
4657     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
4658     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4659     ImVec2 size_expected = pos_max - pos_min;
4660     ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
4661     *out_pos = pos_min;
4662     if (corner_norm.x == 0.0f)
4663         out_pos->x -= (size_constrained.x - size_expected.x);
4664     if (corner_norm.y == 0.0f)
4665         out_pos->y -= (size_constrained.y - size_expected.y);
4666     *out_size = size_constrained;
4667 }
4668 
4669 struct ImGuiResizeGripDef
4670 {
4671     ImVec2  CornerPosN;
4672     ImVec2  InnerDir;
4673     int     AngleMin12, AngleMax12;
4674 };
4675 
4676 static const ImGuiResizeGripDef resize_grip_def[4] =
4677 {
4678     { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4679     { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4680     { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4681     { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4682 };
4683 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4684 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4685 {
4686     ImRect rect = window->Rect();
4687     if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4688     if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);      // Top
4689     if (border_n == 1) return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding);   // Right
4690     if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);      // Bottom
4691     if (border_n == 3) return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding);   // Left
4692     IM_ASSERT(0);
4693     return ImRect();
4694 }
4695 
4696 // 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])4697 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4698 {
4699     ImGuiContext& g = *GImGui;
4700     ImGuiWindowFlags flags = window->Flags;
4701     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4702         return;
4703     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4704         return;
4705 
4706     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4707     const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4708     const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f);
4709     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4710 
4711     ImVec2 pos_target(FLT_MAX, FLT_MAX);
4712     ImVec2 size_target(FLT_MAX, FLT_MAX);
4713 
4714     // Manual resize grips
4715     PushID("#RESIZE");
4716     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4717     {
4718         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4719         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4720 
4721         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4722         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4723         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4724         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4725         bool hovered, held;
4726         ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4727         //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4728         if (hovered || held)
4729             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4730 
4731         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4732         {
4733             // Manual auto-fit when double-clicking
4734             size_target = CalcSizeAfterConstraint(window, size_auto_fit);
4735             ClearActiveID();
4736         }
4737         else if (held)
4738         {
4739             // Resize from any of the four corners
4740             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4741             ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip
4742             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
4743         }
4744         if (resize_grip_n == 0 || held || hovered)
4745             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4746     }
4747     for (int border_n = 0; border_n < resize_border_count; border_n++)
4748     {
4749         bool hovered, held;
4750         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4751         ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4752         //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4753         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4754         {
4755             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4756             if (held)
4757                 *border_held = border_n;
4758         }
4759         if (held)
4760         {
4761             ImVec2 border_target = window->Pos;
4762             ImVec2 border_posn;
4763             if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top
4764             if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right
4765             if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom
4766             if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left
4767             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4768         }
4769     }
4770     PopID();
4771 
4772     // Navigation resize (keyboard/gamepad)
4773     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4774     {
4775         ImVec2 nav_resize_delta;
4776         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4777             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4778         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4779             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4780         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4781         {
4782             const float NAV_RESIZE_SPEED = 600.0f;
4783             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4784             g.NavWindowingToggleLayer = false;
4785             g.NavDisableMouseHover = true;
4786             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4787             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4788             size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4789         }
4790     }
4791 
4792     // Apply back modified position/size to window
4793     if (size_target.x != FLT_MAX)
4794     {
4795         window->SizeFull = size_target;
4796         MarkIniSettingsDirty(window);
4797     }
4798     if (pos_target.x != FLT_MAX)
4799     {
4800         window->Pos = ImFloor(pos_target);
4801         MarkIniSettingsDirty(window);
4802     }
4803 
4804     window->Size = window->SizeFull;
4805 }
4806 
RenderOuterBorders(ImGuiWindow * window)4807 static void ImGui::RenderOuterBorders(ImGuiWindow* window)
4808 {
4809     ImGuiContext& g = *GImGui;
4810     float rounding = window->WindowRounding;
4811     float border_size = window->WindowBorderSize;
4812     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
4813         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
4814 
4815     int border_held = window->ResizeBorderHeld;
4816     if (border_held != -1)
4817     {
4818         struct ImGuiResizeBorderDef
4819         {
4820             ImVec2 InnerDir;
4821             ImVec2 CornerPosN1, CornerPosN2;
4822             float  OuterAngle;
4823         };
4824         static const ImGuiResizeBorderDef resize_border_def[4] =
4825         {
4826             { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
4827             { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
4828             { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
4829             { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f }  // Left
4830         };
4831         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
4832         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
4833         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle);
4834         window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f);
4835         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
4836     }
4837     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
4838     {
4839         float y = window->Pos.y + window->TitleBarHeight() - 1;
4840         window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize);
4841     }
4842 }
4843 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)4844 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4845 {
4846     window->ParentWindow = parent_window;
4847     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4848     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4849         window->RootWindow = parent_window->RootWindow;
4850     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4851         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4852     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4853     {
4854         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
4855         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4856     }
4857 }
4858 
4859 // Push a new ImGui window to add widgets to.
4860 // - 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.
4861 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4862 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4863 //   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.
4864 // - 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.
4865 // - 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)4866 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4867 {
4868     ImGuiContext& g = *GImGui;
4869     const ImGuiStyle& style = g.Style;
4870     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
4871     IM_ASSERT(g.FrameScopeActive);                  // Forgot to call ImGui::NewFrame()
4872     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4873 
4874     // Find or create
4875     ImGuiWindow* window = FindWindowByName(name);
4876     const bool window_just_created = (window == NULL);
4877     if (window_just_created)
4878     {
4879         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.
4880         window = CreateNewWindow(name, size_on_first_use, flags);
4881     }
4882 
4883     // Automatically disable manual moving/resizing when NoInputs is set
4884     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
4885         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4886 
4887     if (flags & ImGuiWindowFlags_NavFlattened)
4888         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4889 
4890     const int current_frame = g.FrameCount;
4891     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4892 
4893     // Update Flags, LastFrameActive, BeginOrderXXX fields
4894     if (first_begin_of_the_frame)
4895         window->Flags = (ImGuiWindowFlags)flags;
4896     else
4897         flags = window->Flags;
4898 
4899     // 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
4900     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4901     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4902     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4903     window->HasCloseButton = (p_open != NULL);
4904 
4905     // Update the Appearing flag
4906     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4907     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4908     if (flags & ImGuiWindowFlags_Popup)
4909     {
4910         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4911         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4912         window_just_activated_by_user |= (window != popup_ref.Window);
4913     }
4914     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4915     if (window->Appearing)
4916         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4917 
4918     // Add to stack
4919     g.CurrentWindowStack.push_back(window);
4920     SetCurrentWindow(window);
4921     CheckStacksSize(window, true);
4922     if (flags & ImGuiWindowFlags_Popup)
4923     {
4924         ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4925         popup_ref.Window = window;
4926         g.BeginPopupStack.push_back(popup_ref);
4927         window->PopupId = popup_ref.PopupId;
4928     }
4929 
4930     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4931         window->NavLastIds[0] = 0;
4932 
4933     // Process SetNextWindow***() calls
4934     bool window_pos_set_by_api = false;
4935     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4936     if (g.NextWindowData.PosCond)
4937     {
4938         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4939         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
4940         {
4941             // May be processed on the next frame if this is our first frame and we are measuring size
4942             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4943             window->SetWindowPosVal = g.NextWindowData.PosVal;
4944             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4945             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4946         }
4947         else
4948         {
4949             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
4950         }
4951     }
4952     if (g.NextWindowData.SizeCond)
4953     {
4954         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4955         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4956         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
4957     }
4958     if (g.NextWindowData.ContentSizeCond)
4959     {
4960         // Adjust passed "client size" to become a "window size"
4961         window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4962         if (window->SizeContentsExplicit.y != 0.0f)
4963             window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4964     }
4965     else if (first_begin_of_the_frame)
4966     {
4967         window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4968     }
4969     if (g.NextWindowData.CollapsedCond)
4970         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
4971     if (g.NextWindowData.FocusCond)
4972         FocusWindow(window);
4973     if (window->Appearing)
4974         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4975 
4976     // When reusing window again multiple times a frame, just append content (don't need to setup again)
4977     if (first_begin_of_the_frame)
4978     {
4979         // Initialize
4980         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4981         UpdateWindowParentAndRootLinks(window, flags, parent_window);
4982 
4983         window->Active = true;
4984         window->BeginOrderWithinParent = 0;
4985         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
4986         window->BeginCount = 0;
4987         window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4988         window->LastFrameActive = current_frame;
4989         window->IDStack.resize(1);
4990 
4991         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
4992         // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.
4993         bool window_title_visible_elsewhere = false;
4994         if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
4995             window_title_visible_elsewhere = true;
4996         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
4997         {
4998             size_t buf_len = (size_t)window->NameBufLen;
4999             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5000             window->NameBufLen = (int)buf_len;
5001         }
5002 
5003         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5004 
5005         // Update contents size from last frame for auto-fitting (or use explicit size)
5006         window->SizeContents = CalcSizeContents(window);
5007         if (window->HiddenFramesRegular > 0)
5008             window->HiddenFramesRegular--;
5009         if (window->HiddenFramesForResize > 0)
5010             window->HiddenFramesForResize--;
5011 
5012         // Hide new windows for one frame until they calculate their size
5013         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5014             window->HiddenFramesForResize = 1;
5015 
5016         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5017         // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5018         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5019         {
5020             window->HiddenFramesForResize = 1;
5021             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5022             {
5023                 if (!window_size_x_set_by_api)
5024                     window->Size.x = window->SizeFull.x = 0.f;
5025                 if (!window_size_y_set_by_api)
5026                     window->Size.y = window->SizeFull.y = 0.f;
5027                 window->SizeContents = ImVec2(0.f, 0.f);
5028             }
5029         }
5030 
5031         SetCurrentWindow(window);
5032 
5033         // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
5034         window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5035         window->WindowPadding = style.WindowPadding;
5036         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5037             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5038         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5039         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5040 
5041         // Collapse window by double-clicking on title bar
5042         // 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
5043         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5044         {
5045             // 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.
5046             ImRect title_bar_rect = window->TitleBarRect();
5047             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5048                 window->WantCollapseToggle = true;
5049             if (window->WantCollapseToggle)
5050             {
5051                 window->Collapsed = !window->Collapsed;
5052                 MarkIniSettingsDirty(window);
5053                 FocusWindow(window);
5054             }
5055         }
5056         else
5057         {
5058             window->Collapsed = false;
5059         }
5060         window->WantCollapseToggle = false;
5061 
5062         // SIZE
5063 
5064         // Calculate auto-fit size, handle automatic resize
5065         const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
5066         ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
5067         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5068         {
5069             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5070             if (!window_size_x_set_by_api)
5071                 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
5072             if (!window_size_y_set_by_api)
5073                 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
5074         }
5075         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5076         {
5077             // Auto-fit may only grow window during the first few frames
5078             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5079             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5080                 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5081             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5082                 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5083             if (!window->Collapsed)
5084                 MarkIniSettingsDirty(window);
5085         }
5086 
5087         // Apply minimum/maximum window size constraints and final size
5088         window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
5089         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5090 
5091         // SCROLLBAR STATUS
5092 
5093         // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
5094         if (!window->Collapsed)
5095         {
5096             // When reading the current size we need to read it after size constraints have been applied
5097             float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
5098             float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
5099             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5100             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
5101             if (window->ScrollbarX && !window->ScrollbarY)
5102                 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
5103             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5104         }
5105 
5106         // POSITION
5107 
5108         // Popup latch its initial position, will position itself when it appears next frame
5109         if (window_just_activated_by_user)
5110         {
5111             window->AutoPosLastDirection = ImGuiDir_None;
5112             if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5113                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5114         }
5115 
5116         // Position child window
5117         if (flags & ImGuiWindowFlags_ChildWindow)
5118         {
5119             IM_ASSERT(parent_window && parent_window->Active);
5120             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5121             parent_window->DC.ChildWindows.push_back(window);
5122             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5123                 window->Pos = parent_window->DC.CursorPos;
5124         }
5125 
5126         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
5127         if (window_pos_with_pivot)
5128             SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
5129         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5130             window->Pos = FindBestWindowPosForPopup(window);
5131         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5132             window->Pos = FindBestWindowPosForPopup(window);
5133         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5134             window->Pos = FindBestWindowPosForPopup(window);
5135 
5136         // Clamp position so it stays visible
5137         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5138         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5139         {
5140             if (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.
5141             {
5142                 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5143                 ImVec2 size_for_clamping = ((g.IO.ConfigWindowsMoveFromTitleBarOnly) && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5144                 window->Pos = ImMax(window->Pos + size_for_clamping, padding) - size_for_clamping;
5145                 window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
5146             }
5147         }
5148         window->Pos = ImFloor(window->Pos);
5149 
5150         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5151         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5152 
5153         // Prepare for item focus requests
5154         window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
5155         window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
5156         window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
5157         window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
5158 
5159         // Apply scrolling
5160         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5161         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5162 
5163         // Apply window focus (new and reactivated windows are moved to front)
5164         bool want_focus = false;
5165         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5166         {
5167             if (flags & ImGuiWindowFlags_Popup)
5168                 want_focus = true;
5169             else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5170                 want_focus = true;
5171         }
5172 
5173         // Handle manual resize: Resize Grips, Borders, Gamepad
5174         int border_held = -1;
5175         ImU32 resize_grip_col[4] = { 0 };
5176         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4
5177         const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
5178         if (!window->Collapsed)
5179             UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
5180         window->ResizeBorderHeld = (signed char)border_held;
5181 
5182         // Default item width. Make it proportional to window size if window manually resizes
5183         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5184             window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
5185         else
5186             window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
5187 
5188         // DRAWING
5189 
5190         // Setup draw list and outer clipping rectangle
5191         window->DrawList->Clear();
5192         window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
5193         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5194         ImRect viewport_rect(GetViewportRect());
5195         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5196             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
5197         else
5198             PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
5199 
5200         // Draw modal window background (darkens what is behind them, all viewports)
5201         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
5202         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5203         if (dim_bg_for_modal || dim_bg_for_window_list)
5204         {
5205             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5206             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5207         }
5208 
5209         // Draw navigation selection/windowing rectangle background
5210         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5211         {
5212             ImRect bb = window->Rect();
5213             bb.Expand(g.FontSize);
5214             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5215                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5216         }
5217 
5218         // Draw window + handle manual resize
5219         // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame.
5220         const float window_rounding = window->WindowRounding;
5221         const float window_border_size = window->WindowBorderSize;
5222         const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5223         const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5224         const ImRect title_bar_rect = window->TitleBarRect();
5225         if (window->Collapsed)
5226         {
5227             // Title bar only
5228             float backup_border_size = style.FrameBorderSize;
5229             g.Style.FrameBorderSize = window->WindowBorderSize;
5230             ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5231             RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5232             g.Style.FrameBorderSize = backup_border_size;
5233         }
5234         else
5235         {
5236             // Window background
5237             if (!(flags & ImGuiWindowFlags_NoBackground))
5238             {
5239                 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5240                 float alpha = 1.0f;
5241                 if (g.NextWindowData.BgAlphaCond != 0)
5242                     alpha = g.NextWindowData.BgAlphaVal;
5243                 if (alpha != 1.0f)
5244                     bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5245                 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5246             }
5247             g.NextWindowData.BgAlphaCond = 0;
5248 
5249             // Title bar
5250             if (!(flags & ImGuiWindowFlags_NoTitleBar))
5251             {
5252                 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5253                 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5254             }
5255 
5256             // Menu bar
5257             if (flags & ImGuiWindowFlags_MenuBar)
5258             {
5259                 ImRect menu_bar_rect = window->MenuBarRect();
5260                 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.
5261                 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5262                 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5263                     window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5264             }
5265 
5266             // Scrollbars
5267             if (window->ScrollbarX)
5268                 Scrollbar(ImGuiLayoutType_Horizontal);
5269             if (window->ScrollbarY)
5270                 Scrollbar(ImGuiLayoutType_Vertical);
5271 
5272             // Render resize grips (after their input handling so we don't have a frame of latency)
5273             if (!(flags & ImGuiWindowFlags_NoResize))
5274             {
5275                 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5276                 {
5277                     const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5278                     const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5279                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
5280                     window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
5281                     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);
5282                     window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5283                 }
5284             }
5285 
5286             // Borders
5287             RenderOuterBorders(window);
5288         }
5289 
5290         // Draw navigation selection/windowing rectangle border
5291         if (g.NavWindowingTargetAnim == window)
5292         {
5293             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5294             ImRect bb = window->Rect();
5295             bb.Expand(g.FontSize);
5296             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5297             {
5298                 bb.Expand(-g.FontSize - 1.0f);
5299                 rounding = window->WindowRounding;
5300             }
5301             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5302         }
5303 
5304         // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
5305         window->SizeFullAtLastBegin = window->SizeFull;
5306 
5307         // Update various regions. Variables they depends on are set above in this function.
5308         // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5309         window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5310         window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
5311         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));
5312         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));
5313 
5314         // Setup drawing context
5315         // (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.)
5316         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5317         window->DC.GroupOffset.x = 0.0f;
5318         window->DC.ColumnsOffset.x = 0.0f;
5319         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
5320         window->DC.CursorPos = window->DC.CursorStartPos;
5321         window->DC.CursorPosPrevLine = window->DC.CursorPos;
5322         window->DC.CursorMaxPos = window->DC.CursorStartPos;
5323         window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5324         window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5325         window->DC.NavHideHighlightOneFrame = false;
5326         window->DC.NavHasScroll = (GetWindowScrollMaxY(window) > 0.0f);
5327         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5328         window->DC.NavLayerActiveMaskNext = 0x00;
5329         window->DC.MenuBarAppending = false;
5330         window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
5331         window->DC.ChildWindows.resize(0);
5332         window->DC.LayoutType = ImGuiLayoutType_Vertical;
5333         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5334         window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5335         window->DC.ItemWidth = window->ItemWidthDefault;
5336         window->DC.TextWrapPos = -1.0f; // disabled
5337         window->DC.ItemFlagsStack.resize(0);
5338         window->DC.ItemWidthStack.resize(0);
5339         window->DC.TextWrapPosStack.resize(0);
5340         window->DC.ColumnsSet = NULL;
5341         window->DC.TreeDepth = 0;
5342         window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
5343         window->DC.StateStorage = &window->StateStorage;
5344         window->DC.GroupStack.resize(0);
5345         window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5346 
5347         if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5348         {
5349             window->DC.ItemFlags = parent_window->DC.ItemFlags;
5350             window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5351         }
5352 
5353         if (window->AutoFitFramesX > 0)
5354             window->AutoFitFramesX--;
5355         if (window->AutoFitFramesY > 0)
5356             window->AutoFitFramesY--;
5357 
5358         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5359         if (want_focus)
5360         {
5361             FocusWindow(window);
5362             NavInitWindow(window, false);
5363         }
5364 
5365         // Title bar
5366         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5367         {
5368             // Close & collapse button are on layer 1 (same as menus) and don't default focus
5369             const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5370             window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5371             window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5372             window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5373 
5374             // Collapse button
5375             if (!(flags & ImGuiWindowFlags_NoCollapse))
5376                 if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos))
5377                     window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
5378 
5379             // Close button
5380             if (p_open != NULL)
5381             {
5382                 const float pad = style.FramePadding.y;
5383                 const float rad = g.FontSize * 0.5f;
5384                 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
5385                     *p_open = false;
5386             }
5387 
5388             window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5389             window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5390             window->DC.ItemFlags = item_flags_backup;
5391 
5392             // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5393             // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code..
5394             const char* UNSAVED_DOCUMENT_MARKER = "*";
5395             float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5396             ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5397             ImRect text_r = title_bar_rect;
5398             float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5399             float pad_right = (p_open == NULL)                     ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5400             if (style.WindowTitleAlign.x > 0.0f)
5401                 pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
5402             text_r.Min.x += pad_left;
5403             text_r.Max.x -= pad_right;
5404             ImRect clip_rect = text_r;
5405             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()
5406             RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
5407             if (flags & ImGuiWindowFlags_UnsavedDocument)
5408             {
5409                 ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5410                 ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f));
5411                 RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect);
5412             }
5413         }
5414 
5415         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5416         window->OuterRectClipped = window->Rect();
5417         window->OuterRectClipped.ClipWith(window->ClipRect);
5418 
5419         // Pressing CTRL+C while holding on a window copy its content to the clipboard
5420         // 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.
5421         // Maybe we can support CTRL+C on every element?
5422         /*
5423         if (g.ActiveId == move_id)
5424             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5425                 LogToClipboard();
5426         */
5427 
5428         // Inner rectangle
5429         // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5430         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5431         window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5432         window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5433         window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5434         window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5435         //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5436 
5437         // Inner clipping rectangle
5438         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5439         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5440         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
5441         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5442         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
5443 
5444         // We fill last item data based on Title Bar, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5445         // This is useful to allow creating context menus on title bar only, etc.
5446         window->DC.LastItemId = window->MoveId;
5447         window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5448         window->DC.LastItemRect = title_bar_rect;
5449     }
5450 
5451     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5452 
5453     // 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)
5454     if (first_begin_of_the_frame)
5455         window->WriteAccessed = false;
5456 
5457     window->BeginCount++;
5458     g.NextWindowData.Clear();
5459 
5460     if (flags & ImGuiWindowFlags_ChildWindow)
5461     {
5462         // Child window can be out of sight and have "negative" clip windows.
5463         // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5464         IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5465         if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5466             if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5467                 window->HiddenFramesRegular = 1;
5468 
5469         // Completely hide along with parent or if parent is collapsed
5470         if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5471             window->HiddenFramesRegular = 1;
5472     }
5473 
5474     // 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)
5475     if (style.Alpha <= 0.0f)
5476         window->HiddenFramesRegular = 1;
5477 
5478     // Update the Hidden flag
5479     window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0);
5480 
5481     // Return false if we don't intend to display anything to allow user to perform an early out optimization
5482     window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5483 
5484     return !window->SkipItems;
5485 }
5486 
5487 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5488 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)5489 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5490 {
5491     // 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.
5492     if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5493         SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
5494 
5495     // Old API feature: override the window background alpha with a parameter.
5496     if (bg_alpha_override >= 0.0f)
5497         SetNextWindowBgAlpha(bg_alpha_override);
5498 
5499     return Begin(name, p_open, flags);
5500 }
5501 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5502 
End()5503 void ImGui::End()
5504 {
5505     ImGuiContext& g = *GImGui;
5506 
5507     if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow)
5508     {
5509         IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!");
5510         return; // FIXME-ERRORHANDLING
5511     }
5512     IM_ASSERT(g.CurrentWindowStack.Size > 0);
5513 
5514     ImGuiWindow* window = g.CurrentWindow;
5515 
5516     if (window->DC.ColumnsSet != NULL)
5517         EndColumns();
5518     PopClipRect();   // Inner window clip rectangle
5519 
5520     // Stop logging
5521     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
5522         LogFinish();
5523 
5524     // Pop from window stack
5525     g.CurrentWindowStack.pop_back();
5526     if (window->Flags & ImGuiWindowFlags_Popup)
5527         g.BeginPopupStack.pop_back();
5528     CheckStacksSize(window, false);
5529     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5530 }
5531 
BringWindowToFocusFront(ImGuiWindow * window)5532 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5533 {
5534     ImGuiContext& g = *GImGui;
5535     if (g.WindowsFocusOrder.back() == window)
5536         return;
5537     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window
5538         if (g.WindowsFocusOrder[i] == window)
5539         {
5540             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5541             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5542             break;
5543         }
5544 }
5545 
BringWindowToDisplayFront(ImGuiWindow * window)5546 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5547 {
5548     ImGuiContext& g = *GImGui;
5549     ImGuiWindow* current_front_window = g.Windows.back();
5550     if (current_front_window == window || current_front_window->RootWindow == window)
5551         return;
5552     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5553         if (g.Windows[i] == window)
5554         {
5555             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5556             g.Windows[g.Windows.Size - 1] = window;
5557             break;
5558         }
5559 }
5560 
BringWindowToDisplayBack(ImGuiWindow * window)5561 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5562 {
5563     ImGuiContext& g = *GImGui;
5564     if (g.Windows[0] == window)
5565         return;
5566     for (int i = 0; i < g.Windows.Size; i++)
5567         if (g.Windows[i] == window)
5568         {
5569             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5570             g.Windows[0] = window;
5571             break;
5572         }
5573 }
5574 
5575 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5576 void ImGui::FocusWindow(ImGuiWindow* window)
5577 {
5578     ImGuiContext& g = *GImGui;
5579 
5580     if (g.NavWindow != window)
5581     {
5582         g.NavWindow = window;
5583         if (window && g.NavDisableMouseHover)
5584             g.NavMousePosDirty = true;
5585         g.NavInitRequest = false;
5586         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5587         g.NavIdIsAlive = false;
5588         g.NavLayer = ImGuiNavLayer_Main;
5589         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
5590     }
5591 
5592     // Passing NULL allow to disable keyboard focus
5593     if (!window)
5594         return;
5595 
5596     // Move the root window to the top of the pile
5597     if (window->RootWindow)
5598         window = window->RootWindow;
5599 
5600     // Steal focus on active widgets
5601     if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5602         if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5603             ClearActiveID();
5604 
5605     // Bring to front
5606     BringWindowToFocusFront(window);
5607     if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5608         BringWindowToDisplayFront(window);
5609 }
5610 
FocusPreviousWindowIgnoringOne(ImGuiWindow * ignore_window)5611 void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
5612 {
5613     ImGuiContext& g = *GImGui;
5614     for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
5615     {
5616         // 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.
5617         ImGuiWindow* window = g.WindowsFocusOrder[i];
5618         if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5619             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5620             {
5621                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5622                 FocusWindow(focus_window);
5623                 return;
5624             }
5625     }
5626 }
5627 
PushItemWidth(float item_width)5628 void ImGui::PushItemWidth(float item_width)
5629 {
5630     ImGuiWindow* window = GetCurrentWindow();
5631     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5632     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5633 }
5634 
PushMultiItemsWidths(int components,float w_full)5635 void ImGui::PushMultiItemsWidths(int components, float w_full)
5636 {
5637     ImGuiWindow* window = GetCurrentWindow();
5638     const ImGuiStyle& style = GImGui->Style;
5639     if (w_full <= 0.0f)
5640         w_full = CalcItemWidth();
5641     const float w_item_one  = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5642     const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5643     window->DC.ItemWidthStack.push_back(w_item_last);
5644     for (int i = 0; i < components-1; i++)
5645         window->DC.ItemWidthStack.push_back(w_item_one);
5646     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5647 }
5648 
PopItemWidth()5649 void ImGui::PopItemWidth()
5650 {
5651     ImGuiWindow* window = GetCurrentWindow();
5652     window->DC.ItemWidthStack.pop_back();
5653     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5654 }
5655 
CalcItemWidth()5656 float ImGui::CalcItemWidth()
5657 {
5658     ImGuiWindow* window = GetCurrentWindowRead();
5659     float w = window->DC.ItemWidth;
5660     if (w < 0.0f)
5661     {
5662         // 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.
5663         float width_to_right_edge = GetContentRegionAvail().x;
5664         w = ImMax(1.0f, width_to_right_edge + w);
5665     }
5666     w = (float)(int)w;
5667     return w;
5668 }
5669 
SetCurrentFont(ImFont * font)5670 void ImGui::SetCurrentFont(ImFont* font)
5671 {
5672     ImGuiContext& g = *GImGui;
5673     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5674     IM_ASSERT(font->Scale > 0.0f);
5675     g.Font = font;
5676     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
5677     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5678 
5679     ImFontAtlas* atlas = g.Font->ContainerAtlas;
5680     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5681     g.DrawListSharedData.Font = g.Font;
5682     g.DrawListSharedData.FontSize = g.FontSize;
5683 }
5684 
PushFont(ImFont * font)5685 void ImGui::PushFont(ImFont* font)
5686 {
5687     ImGuiContext& g = *GImGui;
5688     if (!font)
5689         font = GetDefaultFont();
5690     SetCurrentFont(font);
5691     g.FontStack.push_back(font);
5692     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
5693 }
5694 
PopFont()5695 void  ImGui::PopFont()
5696 {
5697     ImGuiContext& g = *GImGui;
5698     g.CurrentWindow->DrawList->PopTextureID();
5699     g.FontStack.pop_back();
5700     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5701 }
5702 
PushItemFlag(ImGuiItemFlags option,bool enabled)5703 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5704 {
5705     ImGuiWindow* window = GetCurrentWindow();
5706     if (enabled)
5707         window->DC.ItemFlags |= option;
5708     else
5709         window->DC.ItemFlags &= ~option;
5710     window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5711 }
5712 
PopItemFlag()5713 void ImGui::PopItemFlag()
5714 {
5715     ImGuiWindow* window = GetCurrentWindow();
5716     window->DC.ItemFlagsStack.pop_back();
5717     window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5718 }
5719 
5720 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)5721 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5722 {
5723     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
5724 }
5725 
PopAllowKeyboardFocus()5726 void ImGui::PopAllowKeyboardFocus()
5727 {
5728     PopItemFlag();
5729 }
5730 
PushButtonRepeat(bool repeat)5731 void ImGui::PushButtonRepeat(bool repeat)
5732 {
5733     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5734 }
5735 
PopButtonRepeat()5736 void ImGui::PopButtonRepeat()
5737 {
5738     PopItemFlag();
5739 }
5740 
PushTextWrapPos(float wrap_pos_x)5741 void ImGui::PushTextWrapPos(float wrap_pos_x)
5742 {
5743     ImGuiWindow* window = GetCurrentWindow();
5744     window->DC.TextWrapPos = wrap_pos_x;
5745     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5746 }
5747 
PopTextWrapPos()5748 void ImGui::PopTextWrapPos()
5749 {
5750     ImGuiWindow* window = GetCurrentWindow();
5751     window->DC.TextWrapPosStack.pop_back();
5752     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5753 }
5754 
5755 // 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)5756 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5757 {
5758     ImGuiContext& g = *GImGui;
5759     ImGuiColorMod backup;
5760     backup.Col = idx;
5761     backup.BackupValue = g.Style.Colors[idx];
5762     g.ColorModifiers.push_back(backup);
5763     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5764 }
5765 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)5766 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5767 {
5768     ImGuiContext& g = *GImGui;
5769     ImGuiColorMod backup;
5770     backup.Col = idx;
5771     backup.BackupValue = g.Style.Colors[idx];
5772     g.ColorModifiers.push_back(backup);
5773     g.Style.Colors[idx] = col;
5774 }
5775 
PopStyleColor(int count)5776 void ImGui::PopStyleColor(int count)
5777 {
5778     ImGuiContext& g = *GImGui;
5779     while (count > 0)
5780     {
5781         ImGuiColorMod& backup = g.ColorModifiers.back();
5782         g.Style.Colors[backup.Col] = backup.BackupValue;
5783         g.ColorModifiers.pop_back();
5784         count--;
5785     }
5786 }
5787 
5788 struct ImGuiStyleVarInfo
5789 {
5790     ImGuiDataType   Type;
5791     ImU32           Count;
5792     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo5793     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5794 };
5795 
5796 static const ImGuiStyleVarInfo GStyleVarInfo[] =
5797 {
5798     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
5799     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
5800     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
5801     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
5802     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
5803     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
5804     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
5805     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
5806     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
5807     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
5808     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
5809     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
5810     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
5811     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
5812     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
5813     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
5814     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
5815     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
5816     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
5817     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
5818     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
5819     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
5820     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
5821 };
5822 
GetStyleVarInfo(ImGuiStyleVar idx)5823 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5824 {
5825     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5826     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5827     return &GStyleVarInfo[idx];
5828 }
5829 
PushStyleVar(ImGuiStyleVar idx,float val)5830 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5831 {
5832     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5833     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5834     {
5835         ImGuiContext& g = *GImGui;
5836         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5837         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5838         *pvar = val;
5839         return;
5840     }
5841     IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5842 }
5843 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)5844 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5845 {
5846     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5847     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5848     {
5849         ImGuiContext& g = *GImGui;
5850         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5851         g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5852         *pvar = val;
5853         return;
5854     }
5855     IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5856 }
5857 
PopStyleVar(int count)5858 void ImGui::PopStyleVar(int count)
5859 {
5860     ImGuiContext& g = *GImGui;
5861     while (count > 0)
5862     {
5863         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5864         ImGuiStyleMod& backup = g.StyleModifiers.back();
5865         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5866         void* data = info->GetVarPtr(&g.Style);
5867         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
5868         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5869         g.StyleModifiers.pop_back();
5870         count--;
5871     }
5872 }
5873 
GetStyleColorName(ImGuiCol idx)5874 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5875 {
5876     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5877     switch (idx)
5878     {
5879     case ImGuiCol_Text: return "Text";
5880     case ImGuiCol_TextDisabled: return "TextDisabled";
5881     case ImGuiCol_WindowBg: return "WindowBg";
5882     case ImGuiCol_ChildBg: return "ChildBg";
5883     case ImGuiCol_PopupBg: return "PopupBg";
5884     case ImGuiCol_Border: return "Border";
5885     case ImGuiCol_BorderShadow: return "BorderShadow";
5886     case ImGuiCol_FrameBg: return "FrameBg";
5887     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5888     case ImGuiCol_FrameBgActive: return "FrameBgActive";
5889     case ImGuiCol_TitleBg: return "TitleBg";
5890     case ImGuiCol_TitleBgActive: return "TitleBgActive";
5891     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5892     case ImGuiCol_MenuBarBg: return "MenuBarBg";
5893     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5894     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5895     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5896     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5897     case ImGuiCol_CheckMark: return "CheckMark";
5898     case ImGuiCol_SliderGrab: return "SliderGrab";
5899     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5900     case ImGuiCol_Button: return "Button";
5901     case ImGuiCol_ButtonHovered: return "ButtonHovered";
5902     case ImGuiCol_ButtonActive: return "ButtonActive";
5903     case ImGuiCol_Header: return "Header";
5904     case ImGuiCol_HeaderHovered: return "HeaderHovered";
5905     case ImGuiCol_HeaderActive: return "HeaderActive";
5906     case ImGuiCol_Separator: return "Separator";
5907     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5908     case ImGuiCol_SeparatorActive: return "SeparatorActive";
5909     case ImGuiCol_ResizeGrip: return "ResizeGrip";
5910     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5911     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5912     case ImGuiCol_Tab: return "Tab";
5913     case ImGuiCol_TabHovered: return "TabHovered";
5914     case ImGuiCol_TabActive: return "TabActive";
5915     case ImGuiCol_TabUnfocused: return "TabUnfocused";
5916     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
5917     case ImGuiCol_PlotLines: return "PlotLines";
5918     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5919     case ImGuiCol_PlotHistogram: return "PlotHistogram";
5920     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5921     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5922     case ImGuiCol_DragDropTarget: return "DragDropTarget";
5923     case ImGuiCol_NavHighlight: return "NavHighlight";
5924     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5925     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5926     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5927     }
5928     IM_ASSERT(0);
5929     return "Unknown";
5930 }
5931 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)5932 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5933 {
5934     if (window->RootWindow == potential_parent)
5935         return true;
5936     while (window != NULL)
5937     {
5938         if (window == potential_parent)
5939             return true;
5940         window = window->ParentWindow;
5941     }
5942     return false;
5943 }
5944 
IsWindowHovered(ImGuiHoveredFlags flags)5945 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5946 {
5947     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
5948     ImGuiContext& g = *GImGui;
5949 
5950     if (flags & ImGuiHoveredFlags_AnyWindow)
5951     {
5952         if (g.HoveredWindow == NULL)
5953             return false;
5954     }
5955     else
5956     {
5957         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5958         {
5959         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5960             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5961                 return false;
5962             break;
5963         case ImGuiHoveredFlags_RootWindow:
5964             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5965                 return false;
5966             break;
5967         case ImGuiHoveredFlags_ChildWindows:
5968             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
5969                 return false;
5970             break;
5971         default:
5972             if (g.HoveredWindow != g.CurrentWindow)
5973                 return false;
5974             break;
5975         }
5976     }
5977 
5978     if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5979         return false;
5980     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5981         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5982             return false;
5983     return true;
5984 }
5985 
IsWindowFocused(ImGuiFocusedFlags flags)5986 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5987 {
5988     ImGuiContext& g = *GImGui;
5989 
5990     if (flags & ImGuiFocusedFlags_AnyWindow)
5991         return g.NavWindow != NULL;
5992 
5993     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
5994     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5995     {
5996     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5997         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5998     case ImGuiFocusedFlags_RootWindow:
5999         return g.NavWindow == g.CurrentWindow->RootWindow;
6000     case ImGuiFocusedFlags_ChildWindows:
6001         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6002     default:
6003         return g.NavWindow == g.CurrentWindow;
6004     }
6005 }
6006 
6007 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6008 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6009 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6010 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6011 {
6012     return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6013 }
6014 
GetWindowWidth()6015 float ImGui::GetWindowWidth()
6016 {
6017     ImGuiWindow* window = GImGui->CurrentWindow;
6018     return window->Size.x;
6019 }
6020 
GetWindowHeight()6021 float ImGui::GetWindowHeight()
6022 {
6023     ImGuiWindow* window = GImGui->CurrentWindow;
6024     return window->Size.y;
6025 }
6026 
GetWindowPos()6027 ImVec2 ImGui::GetWindowPos()
6028 {
6029     ImGuiContext& g = *GImGui;
6030     ImGuiWindow* window = g.CurrentWindow;
6031     return window->Pos;
6032 }
6033 
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)6034 void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
6035 {
6036     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.
6037     window->Scroll.x = new_scroll_x;
6038     window->DC.CursorMaxPos.x -= window->Scroll.x;
6039 }
6040 
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)6041 void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
6042 {
6043     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.
6044     window->Scroll.y = new_scroll_y;
6045     window->DC.CursorMaxPos.y -= window->Scroll.y;
6046 }
6047 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6048 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6049 {
6050     // Test condition (NB: bit 0 is always true) and clear flags for next time
6051     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6052         return;
6053 
6054     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6055     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6056     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6057 
6058     // Set
6059     const ImVec2 old_pos = window->Pos;
6060     window->Pos = ImFloor(pos);
6061     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
6062     window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
6063 }
6064 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6065 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6066 {
6067     ImGuiWindow* window = GetCurrentWindowRead();
6068     SetWindowPos(window, pos, cond);
6069 }
6070 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6071 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6072 {
6073     if (ImGuiWindow* window = FindWindowByName(name))
6074         SetWindowPos(window, pos, cond);
6075 }
6076 
GetWindowSize()6077 ImVec2 ImGui::GetWindowSize()
6078 {
6079     ImGuiWindow* window = GetCurrentWindowRead();
6080     return window->Size;
6081 }
6082 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6083 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6084 {
6085     // Test condition (NB: bit 0 is always true) and clear flags for next time
6086     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6087         return;
6088 
6089     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6090     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6091 
6092     // Set
6093     if (size.x > 0.0f)
6094     {
6095         window->AutoFitFramesX = 0;
6096         window->SizeFull.x = ImFloor(size.x);
6097     }
6098     else
6099     {
6100         window->AutoFitFramesX = 2;
6101         window->AutoFitOnlyGrows = false;
6102     }
6103     if (size.y > 0.0f)
6104     {
6105         window->AutoFitFramesY = 0;
6106         window->SizeFull.y = ImFloor(size.y);
6107     }
6108     else
6109     {
6110         window->AutoFitFramesY = 2;
6111         window->AutoFitOnlyGrows = false;
6112     }
6113 }
6114 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6115 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6116 {
6117     SetWindowSize(GImGui->CurrentWindow, size, cond);
6118 }
6119 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6120 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6121 {
6122     if (ImGuiWindow* window = FindWindowByName(name))
6123         SetWindowSize(window, size, cond);
6124 }
6125 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6126 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6127 {
6128     // Test condition (NB: bit 0 is always true) and clear flags for next time
6129     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6130         return;
6131     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6132 
6133     // Set
6134     window->Collapsed = collapsed;
6135 }
6136 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6137 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6138 {
6139     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6140 }
6141 
IsWindowCollapsed()6142 bool ImGui::IsWindowCollapsed()
6143 {
6144     ImGuiWindow* window = GetCurrentWindowRead();
6145     return window->Collapsed;
6146 }
6147 
IsWindowAppearing()6148 bool ImGui::IsWindowAppearing()
6149 {
6150     ImGuiWindow* window = GetCurrentWindowRead();
6151     return window->Appearing;
6152 }
6153 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6154 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6155 {
6156     if (ImGuiWindow* window = FindWindowByName(name))
6157         SetWindowCollapsed(window, collapsed, cond);
6158 }
6159 
SetWindowFocus()6160 void ImGui::SetWindowFocus()
6161 {
6162     FocusWindow(GImGui->CurrentWindow);
6163 }
6164 
SetWindowFocus(const char * name)6165 void ImGui::SetWindowFocus(const char* name)
6166 {
6167     if (name)
6168     {
6169         if (ImGuiWindow* window = FindWindowByName(name))
6170             FocusWindow(window);
6171     }
6172     else
6173     {
6174         FocusWindow(NULL);
6175     }
6176 }
6177 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6178 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6179 {
6180     ImGuiContext& g = *GImGui;
6181     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6182     g.NextWindowData.PosVal = pos;
6183     g.NextWindowData.PosPivotVal = pivot;
6184     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6185 }
6186 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6187 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6188 {
6189     ImGuiContext& g = *GImGui;
6190     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6191     g.NextWindowData.SizeVal = size;
6192     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6193 }
6194 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6195 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6196 {
6197     ImGuiContext& g = *GImGui;
6198     g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
6199     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6200     g.NextWindowData.SizeCallback = custom_callback;
6201     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6202 }
6203 
SetNextWindowContentSize(const ImVec2 & size)6204 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6205 {
6206     ImGuiContext& g = *GImGui;
6207     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.
6208     g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
6209 }
6210 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6211 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6212 {
6213     ImGuiContext& g = *GImGui;
6214     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6215     g.NextWindowData.CollapsedVal = collapsed;
6216     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6217 }
6218 
SetNextWindowFocus()6219 void ImGui::SetNextWindowFocus()
6220 {
6221     ImGuiContext& g = *GImGui;
6222     g.NextWindowData.FocusCond = ImGuiCond_Always;   // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6223 }
6224 
SetNextWindowBgAlpha(float alpha)6225 void ImGui::SetNextWindowBgAlpha(float alpha)
6226 {
6227     ImGuiContext& g = *GImGui;
6228     g.NextWindowData.BgAlphaVal = alpha;
6229     g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6230 }
6231 
6232 // FIXME: This is in window space (not screen space!)
GetContentRegionMax()6233 ImVec2 ImGui::GetContentRegionMax()
6234 {
6235     ImGuiWindow* window = GetCurrentWindowRead();
6236     ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
6237     if (window->DC.ColumnsSet)
6238         mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
6239     return mx;
6240 }
6241 
GetContentRegionAvail()6242 ImVec2 ImGui::GetContentRegionAvail()
6243 {
6244     ImGuiWindow* window = GetCurrentWindowRead();
6245     return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
6246 }
6247 
GetContentRegionAvailWidth()6248 float ImGui::GetContentRegionAvailWidth()
6249 {
6250     return GetContentRegionAvail().x;
6251 }
6252 
6253 // In window space (not screen space!)
GetWindowContentRegionMin()6254 ImVec2 ImGui::GetWindowContentRegionMin()
6255 {
6256     ImGuiWindow* window = GetCurrentWindowRead();
6257     return window->ContentsRegionRect.Min - window->Pos;
6258 }
6259 
GetWindowContentRegionMax()6260 ImVec2 ImGui::GetWindowContentRegionMax()
6261 {
6262     ImGuiWindow* window = GetCurrentWindowRead();
6263     return window->ContentsRegionRect.Max - window->Pos;
6264 }
6265 
GetWindowContentRegionWidth()6266 float ImGui::GetWindowContentRegionWidth()
6267 {
6268     ImGuiWindow* window = GetCurrentWindowRead();
6269     return window->ContentsRegionRect.GetWidth();
6270 }
6271 
GetTextLineHeight()6272 float ImGui::GetTextLineHeight()
6273 {
6274     ImGuiContext& g = *GImGui;
6275     return g.FontSize;
6276 }
6277 
GetTextLineHeightWithSpacing()6278 float ImGui::GetTextLineHeightWithSpacing()
6279 {
6280     ImGuiContext& g = *GImGui;
6281     return g.FontSize + g.Style.ItemSpacing.y;
6282 }
6283 
GetFrameHeight()6284 float ImGui::GetFrameHeight()
6285 {
6286     ImGuiContext& g = *GImGui;
6287     return g.FontSize + g.Style.FramePadding.y * 2.0f;
6288 }
6289 
GetFrameHeightWithSpacing()6290 float ImGui::GetFrameHeightWithSpacing()
6291 {
6292     ImGuiContext& g = *GImGui;
6293     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6294 }
6295 
GetWindowDrawList()6296 ImDrawList* ImGui::GetWindowDrawList()
6297 {
6298     ImGuiWindow* window = GetCurrentWindow();
6299     return window->DrawList;
6300 }
6301 
GetFont()6302 ImFont* ImGui::GetFont()
6303 {
6304     return GImGui->Font;
6305 }
6306 
GetFontSize()6307 float ImGui::GetFontSize()
6308 {
6309     return GImGui->FontSize;
6310 }
6311 
GetFontTexUvWhitePixel()6312 ImVec2 ImGui::GetFontTexUvWhitePixel()
6313 {
6314     return GImGui->DrawListSharedData.TexUvWhitePixel;
6315 }
6316 
SetWindowFontScale(float scale)6317 void ImGui::SetWindowFontScale(float scale)
6318 {
6319     ImGuiContext& g = *GImGui;
6320     ImGuiWindow* window = GetCurrentWindow();
6321     window->FontWindowScale = scale;
6322     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6323 }
6324 
6325 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6326 // 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()6327 ImVec2 ImGui::GetCursorPos()
6328 {
6329     ImGuiWindow* window = GetCurrentWindowRead();
6330     return window->DC.CursorPos - window->Pos + window->Scroll;
6331 }
6332 
GetCursorPosX()6333 float ImGui::GetCursorPosX()
6334 {
6335     ImGuiWindow* window = GetCurrentWindowRead();
6336     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6337 }
6338 
GetCursorPosY()6339 float ImGui::GetCursorPosY()
6340 {
6341     ImGuiWindow* window = GetCurrentWindowRead();
6342     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6343 }
6344 
SetCursorPos(const ImVec2 & local_pos)6345 void ImGui::SetCursorPos(const ImVec2& local_pos)
6346 {
6347     ImGuiWindow* window = GetCurrentWindow();
6348     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6349     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6350 }
6351 
SetCursorPosX(float x)6352 void ImGui::SetCursorPosX(float x)
6353 {
6354     ImGuiWindow* window = GetCurrentWindow();
6355     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6356     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6357 }
6358 
SetCursorPosY(float y)6359 void ImGui::SetCursorPosY(float y)
6360 {
6361     ImGuiWindow* window = GetCurrentWindow();
6362     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6363     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6364 }
6365 
GetCursorStartPos()6366 ImVec2 ImGui::GetCursorStartPos()
6367 {
6368     ImGuiWindow* window = GetCurrentWindowRead();
6369     return window->DC.CursorStartPos - window->Pos;
6370 }
6371 
GetCursorScreenPos()6372 ImVec2 ImGui::GetCursorScreenPos()
6373 {
6374     ImGuiWindow* window = GetCurrentWindowRead();
6375     return window->DC.CursorPos;
6376 }
6377 
SetCursorScreenPos(const ImVec2 & pos)6378 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6379 {
6380     ImGuiWindow* window = GetCurrentWindow();
6381     window->DC.CursorPos = pos;
6382     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6383 }
6384 
GetScrollX()6385 float ImGui::GetScrollX()
6386 {
6387     return GImGui->CurrentWindow->Scroll.x;
6388 }
6389 
GetScrollY()6390 float ImGui::GetScrollY()
6391 {
6392     return GImGui->CurrentWindow->Scroll.y;
6393 }
6394 
GetScrollMaxX()6395 float ImGui::GetScrollMaxX()
6396 {
6397     return GetWindowScrollMaxX(GImGui->CurrentWindow);
6398 }
6399 
GetScrollMaxY()6400 float ImGui::GetScrollMaxY()
6401 {
6402     return GetWindowScrollMaxY(GImGui->CurrentWindow);
6403 }
6404 
SetScrollX(float scroll_x)6405 void ImGui::SetScrollX(float scroll_x)
6406 {
6407     ImGuiWindow* window = GetCurrentWindow();
6408     window->ScrollTarget.x = scroll_x;
6409     window->ScrollTargetCenterRatio.x = 0.0f;
6410 }
6411 
SetScrollY(float scroll_y)6412 void ImGui::SetScrollY(float scroll_y)
6413 {
6414     ImGuiWindow* window = GetCurrentWindow();
6415     window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
6416     window->ScrollTargetCenterRatio.y = 0.0f;
6417 }
6418 
SetScrollFromPosY(float local_y,float center_y_ratio)6419 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
6420 {
6421     // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
6422     ImGuiWindow* window = GetCurrentWindow();
6423     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
6424     window->ScrollTarget.y = (float)(int)(local_y + window->Scroll.y);
6425     window->ScrollTargetCenterRatio.y = center_y_ratio;
6426 }
6427 
6428 // 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)6429 void ImGui::SetScrollHereY(float center_y_ratio)
6430 {
6431     ImGuiWindow* window = GetCurrentWindow();
6432     float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
6433     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.
6434     SetScrollFromPosY(target_y, center_y_ratio);
6435 }
6436 
ActivateItem(ImGuiID id)6437 void ImGui::ActivateItem(ImGuiID id)
6438 {
6439     ImGuiContext& g = *GImGui;
6440     g.NavNextActivateId = id;
6441 }
6442 
SetKeyboardFocusHere(int offset)6443 void ImGui::SetKeyboardFocusHere(int offset)
6444 {
6445     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6446     ImGuiWindow* window = GetCurrentWindow();
6447     window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6448     window->FocusIdxTabRequestNext = INT_MAX;
6449 }
6450 
SetItemDefaultFocus()6451 void ImGui::SetItemDefaultFocus()
6452 {
6453     ImGuiContext& g = *GImGui;
6454     ImGuiWindow* window = g.CurrentWindow;
6455     if (!window->Appearing)
6456         return;
6457     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6458     {
6459         g.NavInitRequest = false;
6460         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6461         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6462         NavUpdateAnyRequestFlag();
6463         if (!IsItemVisible())
6464             SetScrollHereY();
6465     }
6466 }
6467 
SetStateStorage(ImGuiStorage * tree)6468 void ImGui::SetStateStorage(ImGuiStorage* tree)
6469 {
6470     ImGuiWindow* window = GImGui->CurrentWindow;
6471     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6472 }
6473 
GetStateStorage()6474 ImGuiStorage* ImGui::GetStateStorage()
6475 {
6476     ImGuiWindow* window = GImGui->CurrentWindow;
6477     return window->DC.StateStorage;
6478 }
6479 
PushID(const char * str_id)6480 void ImGui::PushID(const char* str_id)
6481 {
6482     ImGuiWindow* window = GImGui->CurrentWindow;
6483     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6484 }
6485 
PushID(const char * str_id_begin,const char * str_id_end)6486 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6487 {
6488     ImGuiWindow* window = GImGui->CurrentWindow;
6489     window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6490 }
6491 
PushID(const void * ptr_id)6492 void ImGui::PushID(const void* ptr_id)
6493 {
6494     ImGuiWindow* window = GImGui->CurrentWindow;
6495     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6496 }
6497 
PushID(int int_id)6498 void ImGui::PushID(int int_id)
6499 {
6500     const void* ptr_id = (void*)(intptr_t)int_id;
6501     ImGuiWindow* window = GImGui->CurrentWindow;
6502     window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6503 }
6504 
PopID()6505 void ImGui::PopID()
6506 {
6507     ImGuiWindow* window = GImGui->CurrentWindow;
6508     window->IDStack.pop_back();
6509 }
6510 
GetID(const char * str_id)6511 ImGuiID ImGui::GetID(const char* str_id)
6512 {
6513     ImGuiWindow* window = GImGui->CurrentWindow;
6514     return window->GetID(str_id);
6515 }
6516 
GetID(const char * str_id_begin,const char * str_id_end)6517 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6518 {
6519     ImGuiWindow* window = GImGui->CurrentWindow;
6520     return window->GetID(str_id_begin, str_id_end);
6521 }
6522 
GetID(const void * ptr_id)6523 ImGuiID ImGui::GetID(const void* ptr_id)
6524 {
6525     ImGuiWindow* window = GImGui->CurrentWindow;
6526     return window->GetID(ptr_id);
6527 }
6528 
IsRectVisible(const ImVec2 & size)6529 bool ImGui::IsRectVisible(const ImVec2& size)
6530 {
6531     ImGuiWindow* window = GImGui->CurrentWindow;;
6532     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6533 }
6534 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6535 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6536 {
6537     ImGuiWindow* window = GImGui->CurrentWindow;;
6538     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6539 }
6540 
6541 // 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()6542 void ImGui::BeginGroup()
6543 {
6544     ImGuiContext& g = *GImGui;
6545     ImGuiWindow* window = GetCurrentWindow();
6546 
6547     window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6548     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6549     group_data.BackupCursorPos = window->DC.CursorPos;
6550     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6551     group_data.BackupIndent = window->DC.Indent;
6552     group_data.BackupGroupOffset = window->DC.GroupOffset;
6553     group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6554     group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6555     group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6556     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6557     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6558     group_data.AdvanceCursor = true;
6559 
6560     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6561     window->DC.Indent = window->DC.GroupOffset;
6562     window->DC.CursorMaxPos = window->DC.CursorPos;
6563     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6564     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6565 }
6566 
EndGroup()6567 void ImGui::EndGroup()
6568 {
6569     ImGuiContext& g = *GImGui;
6570     ImGuiWindow* window = GetCurrentWindow();
6571     IM_ASSERT(!window->DC.GroupStack.empty());    // Mismatched BeginGroup()/EndGroup() calls
6572 
6573     ImGuiGroupData& group_data = window->DC.GroupStack.back();
6574 
6575     ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6576     group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
6577 
6578     window->DC.CursorPos = group_data.BackupCursorPos;
6579     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6580     window->DC.Indent = group_data.BackupIndent;
6581     window->DC.GroupOffset = group_data.BackupGroupOffset;
6582     window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6583     window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6584     window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6585 
6586     if (group_data.AdvanceCursor)
6587     {
6588         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.
6589         ItemSize(group_bb.GetSize(), 0.0f);
6590         ItemAdd(group_bb, 0);
6591     }
6592 
6593     // 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.
6594     // 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.
6595     // (and if you grep for LastItemId you'll notice it is only used in that context.
6596     if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6597         window->DC.LastItemId = g.ActiveId;
6598     else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6599         window->DC.LastItemId = g.ActiveIdPreviousFrame;
6600     window->DC.LastItemRect = group_bb;
6601 
6602     window->DC.GroupStack.pop_back();
6603 
6604     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
6605 }
6606 
6607 // Gets back to previous line and continue with horizontal layout
6608 //      pos_x == 0      : follow right after previous item
6609 //      pos_x != 0      : align to specified x position (relative to window/group left)
6610 //      spacing_w < 0   : use default spacing if pos_x == 0, no spacing if pos_x != 0
6611 //      spacing_w >= 0  : enforce spacing amount
SameLine(float pos_x,float spacing_w)6612 void ImGui::SameLine(float pos_x, float spacing_w)
6613 {
6614     ImGuiWindow* window = GetCurrentWindow();
6615     if (window->SkipItems)
6616         return;
6617 
6618     ImGuiContext& g = *GImGui;
6619     if (pos_x != 0.0f)
6620     {
6621         if (spacing_w < 0.0f) spacing_w = 0.0f;
6622         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6623         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6624     }
6625     else
6626     {
6627         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6628         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6629         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6630     }
6631     window->DC.CurrentLineSize = window->DC.PrevLineSize;
6632     window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6633 }
6634 
Indent(float indent_w)6635 void ImGui::Indent(float indent_w)
6636 {
6637     ImGuiContext& g = *GImGui;
6638     ImGuiWindow* window = GetCurrentWindow();
6639     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6640     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6641 }
6642 
Unindent(float indent_w)6643 void ImGui::Unindent(float indent_w)
6644 {
6645     ImGuiContext& g = *GImGui;
6646     ImGuiWindow* window = GetCurrentWindow();
6647     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6648     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6649 }
6650 
6651 //-----------------------------------------------------------------------------
6652 // [SECTION] TOOLTIPS
6653 //-----------------------------------------------------------------------------
6654 
BeginTooltip()6655 void ImGui::BeginTooltip()
6656 {
6657     ImGuiContext& g = *GImGui;
6658     if (g.DragDropWithinSourceOrTarget)
6659     {
6660         // 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)
6661         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6662         // 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.
6663         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6664         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6665         SetNextWindowPos(tooltip_pos);
6666         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6667         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6668         BeginTooltipEx(0, true);
6669     }
6670     else
6671     {
6672         BeginTooltipEx(0, false);
6673     }
6674 }
6675 
6676 // 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)6677 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6678 {
6679     ImGuiContext& g = *GImGui;
6680     char window_name[16];
6681     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
6682     if (override_previous_tooltip)
6683         if (ImGuiWindow* window = FindWindowByName(window_name))
6684             if (window->Active)
6685             {
6686                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6687                 window->Hidden = true;
6688                 window->HiddenFramesRegular = 1;
6689                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
6690             }
6691     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
6692     Begin(window_name, NULL, flags | extra_flags);
6693 }
6694 
EndTooltip()6695 void ImGui::EndTooltip()
6696 {
6697     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
6698     End();
6699 }
6700 
SetTooltipV(const char * fmt,va_list args)6701 void ImGui::SetTooltipV(const char* fmt, va_list args)
6702 {
6703     ImGuiContext& g = *GImGui;
6704     if (g.DragDropWithinSourceOrTarget)
6705         BeginTooltip();
6706     else
6707         BeginTooltipEx(0, true);
6708     TextV(fmt, args);
6709     EndTooltip();
6710 }
6711 
SetTooltip(const char * fmt,...)6712 void ImGui::SetTooltip(const char* fmt, ...)
6713 {
6714     va_list args;
6715     va_start(args, fmt);
6716     SetTooltipV(fmt, args);
6717     va_end(args);
6718 }
6719 
6720 //-----------------------------------------------------------------------------
6721 // [SECTION] POPUPS
6722 //-----------------------------------------------------------------------------
6723 
IsPopupOpen(ImGuiID id)6724 bool ImGui::IsPopupOpen(ImGuiID id)
6725 {
6726     ImGuiContext& g = *GImGui;
6727     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
6728 }
6729 
IsPopupOpen(const char * str_id)6730 bool ImGui::IsPopupOpen(const char* str_id)
6731 {
6732     ImGuiContext& g = *GImGui;
6733     return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
6734 }
6735 
GetFrontMostPopupModal()6736 ImGuiWindow* ImGui::GetFrontMostPopupModal()
6737 {
6738     ImGuiContext& g = *GImGui;
6739     for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6740         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6741             if (popup->Flags & ImGuiWindowFlags_Modal)
6742                 return popup;
6743     return NULL;
6744 }
6745 
OpenPopup(const char * str_id)6746 void ImGui::OpenPopup(const char* str_id)
6747 {
6748     ImGuiContext& g = *GImGui;
6749     OpenPopupEx(g.CurrentWindow->GetID(str_id));
6750 }
6751 
6752 // Mark popup as open (toggle toward open state).
6753 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6754 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6755 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)6756 void ImGui::OpenPopupEx(ImGuiID id)
6757 {
6758     ImGuiContext& g = *GImGui;
6759     ImGuiWindow* parent_window = g.CurrentWindow;
6760     int current_stack_size = g.BeginPopupStack.Size;
6761     ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6762     popup_ref.PopupId = id;
6763     popup_ref.Window = NULL;
6764     popup_ref.ParentWindow = parent_window;
6765     popup_ref.OpenFrameCount = g.FrameCount;
6766     popup_ref.OpenParentId = parent_window->IDStack.back();
6767     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6768     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
6769 
6770     //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6771     if (g.OpenPopupStack.Size < current_stack_size + 1)
6772     {
6773         g.OpenPopupStack.push_back(popup_ref);
6774     }
6775     else
6776     {
6777         // 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
6778         // 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
6779         // 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.
6780         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6781         {
6782             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6783         }
6784         else
6785         {
6786             // Close child popups if any, then flag popup for open/reopen
6787             g.OpenPopupStack.resize(current_stack_size + 1);
6788             g.OpenPopupStack[current_stack_size] = popup_ref;
6789         }
6790 
6791         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6792         // This is equivalent to what ClosePopupToLevel() does.
6793         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
6794         //    FocusWindow(parent_window);
6795     }
6796 }
6797 
OpenPopupOnItemClick(const char * str_id,int mouse_button)6798 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6799 {
6800     ImGuiWindow* window = GImGui->CurrentWindow;
6801     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6802     {
6803         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!
6804         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6805         OpenPopupEx(id);
6806         return true;
6807     }
6808     return false;
6809 }
6810 
ClosePopupsOverWindow(ImGuiWindow * ref_window)6811 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6812 {
6813     ImGuiContext& g = *GImGui;
6814     if (g.OpenPopupStack.empty())
6815         return;
6816 
6817     // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6818     // Don't close our own child popup windows.
6819     int popup_count_to_keep = 0;
6820     if (ref_window)
6821     {
6822         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
6823         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
6824         {
6825             ImGuiPopupRef& popup = g.OpenPopupStack[popup_count_to_keep];
6826             if (!popup.Window)
6827                 continue;
6828             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6829             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6830                 continue;
6831 
6832             // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6833             bool popup_or_descendent_has_focus = false;
6834             for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_has_focus; m++)
6835                 if (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow)
6836                     popup_or_descendent_has_focus = true;
6837             if (!popup_or_descendent_has_focus)
6838                 break;
6839         }
6840     }
6841     if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
6842     {
6843         //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
6844         ClosePopupToLevel(popup_count_to_keep, false);
6845     }
6846 }
6847 
ClosePopupToLevel(int remaining,bool apply_focus_to_window_under)6848 void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under)
6849 {
6850     IM_ASSERT(remaining >= 0);
6851     ImGuiContext& g = *GImGui;
6852     ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6853     g.OpenPopupStack.resize(remaining);
6854 
6855     // FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely.
6856     // Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing.
6857     // The current code will set focus to the parent of the popup window which is incorrect.
6858     // It rarely manifested until now because UpdateMouseMovingWindowNewFrame() would call FocusWindow() again on the clicked window,
6859     // leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again.
6860     // However if the clicked window has the _NoMove flag set we would be left with B focused.
6861     // For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway,
6862     // but we should inspect and fix this properly.
6863     if (apply_focus_to_window_under)
6864     {
6865         if (g.NavLayer == 0)
6866             focus_window = NavRestoreLastChildNavWindow(focus_window);
6867         FocusWindow(focus_window);
6868     }
6869 }
6870 
6871 // Close the popup we have begin-ed into.
CloseCurrentPopup()6872 void ImGui::CloseCurrentPopup()
6873 {
6874     ImGuiContext& g = *GImGui;
6875     int popup_idx = g.BeginPopupStack.Size - 1;
6876     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6877         return;
6878 
6879     // Closing a menu closes its top-most parent popup (unless a modal)
6880     while (popup_idx > 0)
6881     {
6882         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
6883         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
6884         bool close_parent = false;
6885         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
6886             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
6887                 close_parent = true;
6888         if (!close_parent)
6889             break;
6890         popup_idx--;
6891     }
6892     //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
6893     ClosePopupToLevel(popup_idx, true);
6894 
6895     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
6896     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
6897     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
6898     if (ImGuiWindow* window = g.NavWindow)
6899         window->DC.NavHideHighlightOneFrame = true;
6900 }
6901 
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)6902 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6903 {
6904     ImGuiContext& g = *GImGui;
6905     if (!IsPopupOpen(id))
6906     {
6907         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6908         return false;
6909     }
6910 
6911     char name[20];
6912     if (extra_flags & ImGuiWindowFlags_ChildMenu)
6913         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
6914     else
6915         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6916 
6917     bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
6918     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6919         EndPopup();
6920 
6921     return is_open;
6922 }
6923 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)6924 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6925 {
6926     ImGuiContext& g = *GImGui;
6927     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
6928     {
6929         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6930         return false;
6931     }
6932     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
6933     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
6934 }
6935 
6936 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
6937 // Note that popup visibility status is owned by imgui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here.
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)6938 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6939 {
6940     ImGuiContext& g = *GImGui;
6941     ImGuiWindow* window = g.CurrentWindow;
6942     const ImGuiID id = window->GetID(name);
6943     if (!IsPopupOpen(id))
6944     {
6945         g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6946         return false;
6947     }
6948 
6949     // Center modal windows by default
6950     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6951     if (g.NextWindowData.PosCond == 0)
6952         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
6953 
6954     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
6955     const bool is_open = Begin(name, p_open, flags);
6956     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6957     {
6958         EndPopup();
6959         if (is_open)
6960             ClosePopupToLevel(g.BeginPopupStack.Size, true);
6961         return false;
6962     }
6963     return is_open;
6964 }
6965 
EndPopup()6966 void ImGui::EndPopup()
6967 {
6968     ImGuiContext& g = *GImGui;
6969     IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
6970     IM_ASSERT(g.BeginPopupStack.Size > 0);
6971 
6972     // Make all menus and popups wrap around for now, may need to expose that policy.
6973     NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
6974 
6975     End();
6976 }
6977 
6978 // This is a helper to handle the simplest case of associating one named popup to one given widget.
6979 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6980 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)6981 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6982 {
6983     ImGuiWindow* window = GImGui->CurrentWindow;
6984     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!
6985     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6986     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6987         OpenPopupEx(id);
6988     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6989 }
6990 
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)6991 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6992 {
6993     if (!str_id)
6994         str_id = "window_context";
6995     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6996     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6997         if (also_over_items || !IsAnyItemHovered())
6998             OpenPopupEx(id);
6999     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7000 }
7001 
BeginPopupContextVoid(const char * str_id,int mouse_button)7002 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
7003 {
7004     if (!str_id)
7005         str_id = "void_context";
7006     ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7007     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7008         OpenPopupEx(id);
7009     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7010 }
7011 
GetWindowAllowedExtentRect(ImGuiWindow *)7012 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
7013 {
7014     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7015     ImRect r_screen = GetViewportRect();
7016     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7017     return r_screen;
7018 }
7019 
7020 // 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.)
7021 // 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)7022 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7023 {
7024     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7025     //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7026     //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7027 
7028     // Combo Box policy (we want a connecting edge)
7029     if (policy == ImGuiPopupPositionPolicy_ComboBox)
7030     {
7031         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7032         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7033         {
7034             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7035             if (n != -1 && dir == *last_dir) // Already tried this direction?
7036                 continue;
7037             ImVec2 pos;
7038             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
7039             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7040             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7041             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7042             if (!r_outer.Contains(ImRect(pos, pos + size)))
7043                 continue;
7044             *last_dir = dir;
7045             return pos;
7046         }
7047     }
7048 
7049     // Default popup policy
7050     const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7051     for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7052     {
7053         const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7054         if (n != -1 && dir == *last_dir) // Already tried this direction?
7055             continue;
7056         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);
7057         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);
7058         if (avail_w < size.x || avail_h < size.y)
7059             continue;
7060         ImVec2 pos;
7061         pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7062         pos.y = (dir == ImGuiDir_Up)   ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down)  ? r_avoid.Max.y : base_pos_clamped.y;
7063         *last_dir = dir;
7064         return pos;
7065     }
7066 
7067     // Fallback, try to keep within display
7068     *last_dir = ImGuiDir_None;
7069     ImVec2 pos = ref_pos;
7070     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7071     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7072     return pos;
7073 }
7074 
FindBestWindowPosForPopup(ImGuiWindow * window)7075 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7076 {
7077     ImGuiContext& g = *GImGui;
7078 
7079     ImRect r_outer = GetWindowAllowedExtentRect(window);
7080     if (window->Flags & ImGuiWindowFlags_ChildMenu)
7081     {
7082         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7083         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7084         IM_ASSERT(g.CurrentWindow == window);
7085         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7086         float horizontal_overlap = g.Style.ItemInnerSpacing.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).
7087         ImRect r_avoid;
7088         if (parent_window->DC.MenuBarAppending)
7089             r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7090         else
7091             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);
7092         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7093     }
7094     if (window->Flags & ImGuiWindowFlags_Popup)
7095     {
7096         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7097         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7098     }
7099     if (window->Flags & ImGuiWindowFlags_Tooltip)
7100     {
7101         // Position tooltip (always follows mouse)
7102         float sc = g.Style.MouseCursorScale;
7103         ImVec2 ref_pos = NavCalcPreferredRefPos();
7104         ImRect r_avoid;
7105         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7106             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7107         else
7108             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.
7109         ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7110         if (window->AutoPosLastDirection == ImGuiDir_None)
7111             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.
7112         return pos;
7113     }
7114     IM_ASSERT(0);
7115     return window->Pos;
7116 }
7117 
7118 //-----------------------------------------------------------------------------
7119 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
7120 //-----------------------------------------------------------------------------
7121 
7122 // (this section is filled in the 'viewport' and 'docking' branches)
7123 
7124 //-----------------------------------------------------------------------------
7125 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7126 //-----------------------------------------------------------------------------
7127 
ImGetDirQuadrantFromDelta(float dx,float dy)7128 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7129 {
7130     if (ImFabs(dx) > ImFabs(dy))
7131         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7132     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7133 }
7134 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)7135 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7136 {
7137     if (a1 < b0)
7138         return a1 - b0;
7139     if (b1 < a0)
7140         return a0 - b1;
7141     return 0.0f;
7142 }
7143 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7144 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7145 {
7146     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7147     {
7148         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7149         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7150     }
7151     else
7152     {
7153         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7154         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7155     }
7156 }
7157 
7158 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7159 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7160 {
7161     ImGuiContext& g = *GImGui;
7162     ImGuiWindow* window = g.CurrentWindow;
7163     if (g.NavLayer != window->DC.NavLayerCurrent)
7164         return false;
7165 
7166     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)
7167     g.NavScoringCount++;
7168 
7169     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7170     if (window->ParentWindow == g.NavWindow)
7171     {
7172         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7173         if (!window->ClipRect.Contains(cand))
7174             return false;
7175         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7176     }
7177 
7178     // 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)
7179     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7180     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7181 
7182     // Compute distance between boxes
7183     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7184     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7185     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
7186     if (dby != 0.0f && dbx != 0.0f)
7187        dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7188     float dist_box = ImFabs(dbx) + ImFabs(dby);
7189 
7190     // 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)
7191     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7192     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7193     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7194 
7195     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7196     ImGuiDir quadrant;
7197     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7198     if (dbx != 0.0f || dby != 0.0f)
7199     {
7200         // For non-overlapping boxes, use distance between boxes
7201         dax = dbx;
7202         day = dby;
7203         dist_axial = dist_box;
7204         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7205     }
7206     else if (dcx != 0.0f || dcy != 0.0f)
7207     {
7208         // For overlapping boxes with different centers, use distance between centers
7209         dax = dcx;
7210         day = dcy;
7211         dist_axial = dist_center;
7212         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7213     }
7214     else
7215     {
7216         // 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)
7217         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7218     }
7219 
7220 #if IMGUI_DEBUG_NAV_SCORING
7221     char buf[128];
7222     if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
7223     {
7224         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]);
7225         ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7226         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7227         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7228         draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
7229         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7230     }
7231     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7232     {
7233         if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7234         if (quadrant == g.NavMoveDir)
7235         {
7236             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7237             ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7238             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7239             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7240         }
7241     }
7242  #endif
7243 
7244     // Is it in the quadrant we're interesting in moving to?
7245     bool new_best = false;
7246     if (quadrant == g.NavMoveDir)
7247     {
7248         // Does it beat the current best candidate?
7249         if (dist_box < result->DistBox)
7250         {
7251             result->DistBox = dist_box;
7252             result->DistCenter = dist_center;
7253             return true;
7254         }
7255         if (dist_box == result->DistBox)
7256         {
7257             // Try using distance between center points to break ties
7258             if (dist_center < result->DistCenter)
7259             {
7260                 result->DistCenter = dist_center;
7261                 new_best = true;
7262             }
7263             else if (dist_center == result->DistCenter)
7264             {
7265                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7266                 // (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),
7267                 // 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.
7268                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7269                     new_best = true;
7270             }
7271         }
7272     }
7273 
7274     // 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
7275     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7276     // 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.
7277     // 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.
7278     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7279     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
7280         if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7281             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))
7282             {
7283                 result->DistAxial = dist_axial;
7284                 new_best = true;
7285             }
7286 
7287     return new_best;
7288 }
7289 
7290 // 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)7291 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7292 {
7293     ImGuiContext& g = *GImGui;
7294     //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.
7295     //    return;
7296 
7297     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7298     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7299 
7300     // Process Init Request
7301     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7302     {
7303         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7304         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7305         {
7306             g.NavInitResultId = id;
7307             g.NavInitResultRectRel = nav_bb_rel;
7308         }
7309         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7310         {
7311             g.NavInitRequest = false; // Found a match, clear request
7312             NavUpdateAnyRequestFlag();
7313         }
7314     }
7315 
7316     // Process Move Request (scoring for navigation)
7317     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7318     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
7319     {
7320         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7321 #if IMGUI_DEBUG_NAV_SCORING
7322         // [DEBUG] Score all items in NavWindow at all times
7323         if (!g.NavMoveRequest)
7324             g.NavMoveDir = g.NavMoveDirLast;
7325         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7326 #else
7327         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7328 #endif
7329         if (new_best)
7330         {
7331             result->ID = id;
7332             result->SelectScopeId = g.MultiSelectScopeId;
7333             result->Window = window;
7334             result->RectRel = nav_bb_rel;
7335         }
7336 
7337         const float VISIBLE_RATIO = 0.70f;
7338         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7339             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)
7340                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7341                 {
7342                     result = &g.NavMoveResultLocalVisibleSet;
7343                     result->ID = id;
7344                     result->SelectScopeId = g.MultiSelectScopeId;
7345                     result->Window = window;
7346                     result->RectRel = nav_bb_rel;
7347                 }
7348     }
7349 
7350     // Update window-relative bounding box of navigated item
7351     if (g.NavId == id)
7352     {
7353         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7354         g.NavLayer = window->DC.NavLayerCurrent;
7355         g.NavIdIsAlive = true;
7356         g.NavIdTabCounter = window->FocusIdxTabCounter;
7357         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
7358     }
7359 }
7360 
NavMoveRequestButNoResultYet()7361 bool ImGui::NavMoveRequestButNoResultYet()
7362 {
7363     ImGuiContext& g = *GImGui;
7364     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7365 }
7366 
NavMoveRequestCancel()7367 void ImGui::NavMoveRequestCancel()
7368 {
7369     ImGuiContext& g = *GImGui;
7370     g.NavMoveRequest = false;
7371     NavUpdateAnyRequestFlag();
7372 }
7373 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7374 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7375 {
7376     ImGuiContext& g = *GImGui;
7377     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7378     ImGui::NavMoveRequestCancel();
7379     g.NavMoveDir = move_dir;
7380     g.NavMoveClipDir = clip_dir;
7381     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7382     g.NavMoveRequestFlags = move_flags;
7383     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7384 }
7385 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7386 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7387 {
7388     ImGuiContext& g = *GImGui;
7389     if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7390         return;
7391     IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7392     ImRect bb_rel = window->NavRectRel[0];
7393 
7394     ImGuiDir clip_dir = g.NavMoveDir;
7395     if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7396     {
7397         bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
7398         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7399         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7400     }
7401     if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7402     {
7403         bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7404         if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7405         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7406     }
7407     if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7408     {
7409         bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
7410         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7411         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7412     }
7413     if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7414     {
7415         bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7416         if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7417         NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7418     }
7419 }
7420 
NavSaveLastChildNavWindow(ImGuiWindow * nav_window)7421 static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
7422 {
7423     ImGuiWindow* parent_window = nav_window;
7424     while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7425         parent_window = parent_window->ParentWindow;
7426     if (parent_window && parent_window != nav_window)
7427         parent_window->NavLastChildNavWindow = nav_window;
7428 }
7429 
7430 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)7431 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7432 {
7433     return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7434 }
7435 
NavRestoreLayer(ImGuiNavLayer layer)7436 static void NavRestoreLayer(ImGuiNavLayer layer)
7437 {
7438     ImGuiContext& g = *GImGui;
7439     g.NavLayer = layer;
7440     if (layer == 0)
7441         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
7442     if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7443         ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
7444     else
7445         ImGui::NavInitWindow(g.NavWindow, true);
7446 }
7447 
NavUpdateAnyRequestFlag()7448 static inline void ImGui::NavUpdateAnyRequestFlag()
7449 {
7450     ImGuiContext& g = *GImGui;
7451     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7452     if (g.NavAnyRequest)
7453         IM_ASSERT(g.NavWindow != NULL);
7454 }
7455 
7456 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)7457 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7458 {
7459     ImGuiContext& g = *GImGui;
7460     IM_ASSERT(window == g.NavWindow);
7461     bool init_for_nav = false;
7462     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7463         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7464             init_for_nav = true;
7465     if (init_for_nav)
7466     {
7467         SetNavID(0, g.NavLayer);
7468         g.NavInitRequest = true;
7469         g.NavInitRequestFromMove = false;
7470         g.NavInitResultId = 0;
7471         g.NavInitResultRectRel = ImRect();
7472         NavUpdateAnyRequestFlag();
7473     }
7474     else
7475     {
7476         g.NavId = window->NavLastIds[0];
7477     }
7478 }
7479 
NavCalcPreferredRefPos()7480 static ImVec2 ImGui::NavCalcPreferredRefPos()
7481 {
7482     ImGuiContext& g = *GImGui;
7483     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7484     {
7485         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
7486         if (IsMousePosValid(&g.IO.MousePos))
7487             return g.IO.MousePos;
7488         return g.LastValidMousePos;
7489     }
7490     else
7491     {
7492         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
7493         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7494         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()));
7495         ImRect visible_rect = GetViewportRect();
7496         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.
7497     }
7498 }
7499 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)7500 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7501 {
7502     ImGuiContext& g = *GImGui;
7503     if (mode == ImGuiInputReadMode_Down)
7504         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
7505 
7506     const float t = g.IO.NavInputsDownDuration[n];
7507     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
7508         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7509     if (t < 0.0f)
7510         return 0.0f;
7511     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
7512         return (t == 0.0f) ? 1.0f : 0.0f;
7513     if (mode == ImGuiInputReadMode_Repeat)
7514         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
7515     if (mode == ImGuiInputReadMode_RepeatSlow)
7516         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
7517     if (mode == ImGuiInputReadMode_RepeatFast)
7518         return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
7519     return 0.0f;
7520 }
7521 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)7522 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7523 {
7524     ImVec2 delta(0.0f, 0.0f);
7525     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7526         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
7527     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7528         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
7529     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7530         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
7531     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
7532         delta *= slow_factor;
7533     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
7534         delta *= fast_factor;
7535     return delta;
7536 }
7537 
7538 // Scroll to keep newly navigated item fully into view
7539 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,const ImRect & item_rect)7540 static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7541 {
7542     ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7543     //GetOverlayDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7544     if (window_rect.Contains(item_rect))
7545         return;
7546 
7547     ImGuiContext& g = *GImGui;
7548     if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7549     {
7550         window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7551         window->ScrollTargetCenterRatio.x = 0.0f;
7552     }
7553     else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7554     {
7555         window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7556         window->ScrollTargetCenterRatio.x = 1.0f;
7557     }
7558     if (item_rect.Min.y < window_rect.Min.y)
7559     {
7560         window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7561         window->ScrollTargetCenterRatio.y = 0.0f;
7562     }
7563     else if (item_rect.Max.y >= window_rect.Max.y)
7564     {
7565         window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7566         window->ScrollTargetCenterRatio.y = 1.0f;
7567     }
7568 }
7569 
NavUpdate()7570 static void ImGui::NavUpdate()
7571 {
7572     ImGuiContext& g = *GImGui;
7573     g.IO.WantSetMousePos = false;
7574 #if 0
7575     if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("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);
7576 #endif
7577 
7578     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7579     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7580     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7581     if (nav_gamepad_active)
7582         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)
7583             g.NavInputSource = ImGuiInputSource_NavGamepad;
7584 
7585     // Update Keyboard->Nav inputs mapping
7586     if (nav_keyboard_active)
7587     {
7588         #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7589         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
7590         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
7591         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
7592         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7593         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7594         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
7595         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7596         if (g.IO.KeyCtrl)   g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7597         if (g.IO.KeyShift)  g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7598         if (g.IO.KeyAlt)    g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
7599         #undef NAV_MAP_KEY
7600     }
7601     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
7602     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7603         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;
7604 
7605     // Process navigation init request (select first/default focus)
7606     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7607     {
7608         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7609         IM_ASSERT(g.NavWindow);
7610         if (g.NavInitRequestFromMove)
7611             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
7612         else
7613             SetNavID(g.NavInitResultId, g.NavLayer);
7614         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7615     }
7616     g.NavInitRequest = false;
7617     g.NavInitRequestFromMove = false;
7618     g.NavInitResultId = 0;
7619     g.NavJustMovedToId = 0;
7620 
7621     // Process navigation move request
7622     if (g.NavMoveRequest)
7623         NavUpdateMoveResult();
7624 
7625     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7626     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7627     {
7628         IM_ASSERT(g.NavMoveRequest);
7629         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7630             g.NavDisableHighlight = false;
7631         g.NavMoveRequestForward = ImGuiNavForward_None;
7632     }
7633 
7634     // Apply application mouse position movement, after we had a chance to process move request result.
7635     if (g.NavMousePosDirty && g.NavIdIsAlive)
7636     {
7637         // Set mouse position given our knowledge of the navigated item position from last frame
7638         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7639         {
7640             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7641             {
7642                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7643                 g.IO.WantSetMousePos = true;
7644             }
7645         }
7646         g.NavMousePosDirty = false;
7647     }
7648     g.NavIdIsAlive = false;
7649     g.NavJustTabbedId = 0;
7650     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7651 
7652     // 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
7653     if (g.NavWindow)
7654         NavSaveLastChildNavWindow(g.NavWindow);
7655     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7656         g.NavWindow->NavLastChildNavWindow = NULL;
7657 
7658     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7659     NavUpdateWindowing();
7660 
7661     // Set output flags for user application
7662     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7663     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
7664 
7665     // Process NavCancel input (to close a popup, get back to parent, clear focus)
7666     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
7667     {
7668         if (g.ActiveId != 0)
7669         {
7670             if (!(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_Cancel)))
7671                 ClearActiveID();
7672         }
7673         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7674         {
7675             // Exit child window
7676             ImGuiWindow* child_window = g.NavWindow;
7677             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7678             IM_ASSERT(child_window->ChildId != 0);
7679             FocusWindow(parent_window);
7680             SetNavID(child_window->ChildId, 0);
7681             g.NavIdIsAlive = false;
7682             if (g.NavDisableMouseHover)
7683                 g.NavMousePosDirty = true;
7684         }
7685         else if (g.OpenPopupStack.Size > 0)
7686         {
7687             // Close open popup/menu
7688             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7689                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
7690         }
7691         else if (g.NavLayer != 0)
7692         {
7693             // Leave the "menu" layer
7694             NavRestoreLayer(ImGuiNavLayer_Main);
7695         }
7696         else
7697         {
7698             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7699             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7700                 g.NavWindow->NavLastIds[0] = 0;
7701             g.NavId = 0;
7702         }
7703     }
7704 
7705     // Process manual activation request
7706     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7707     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7708     {
7709         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
7710         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
7711         if (g.ActiveId == 0 && activate_pressed)
7712             g.NavActivateId = g.NavId;
7713         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7714             g.NavActivateDownId = g.NavId;
7715         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7716             g.NavActivatePressedId = g.NavId;
7717         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
7718             g.NavInputId = g.NavId;
7719     }
7720     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7721         g.NavDisableHighlight = true;
7722     if (g.NavActivateId != 0)
7723         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7724     g.NavMoveRequest = false;
7725 
7726     // Process programmatic activation request
7727     if (g.NavNextActivateId != 0)
7728         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7729     g.NavNextActivateId = 0;
7730 
7731     // Initiate directional inputs request
7732     const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7733     if (g.NavMoveRequestForward == ImGuiNavForward_None)
7734     {
7735         g.NavMoveDir = ImGuiDir_None;
7736         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
7737         if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7738         {
7739             if ((allowed_dir_flags & (1<<ImGuiDir_Left))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7740             if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7741             if ((allowed_dir_flags & (1<<ImGuiDir_Up))    && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp,   ImGuiNavInput_KeyUp_,   ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7742             if ((allowed_dir_flags & (1<<ImGuiDir_Down))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7743         }
7744         g.NavMoveClipDir = g.NavMoveDir;
7745     }
7746     else
7747     {
7748         // 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)
7749         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7750         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7751         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7752         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7753     }
7754 
7755     // Update PageUp/PageDown scroll
7756     float nav_scoring_rect_offset_y = 0.0f;
7757     if (nav_keyboard_active)
7758         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7759 
7760     // 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
7761     if (g.NavMoveDir != ImGuiDir_None)
7762     {
7763         g.NavMoveRequest = true;
7764         g.NavMoveDirLast = g.NavMoveDir;
7765     }
7766     if (g.NavMoveRequest && g.NavId == 0)
7767     {
7768         g.NavInitRequest = g.NavInitRequestFromMove = true;
7769         g.NavInitResultId = 0;
7770         g.NavDisableHighlight = false;
7771     }
7772     NavUpdateAnyRequestFlag();
7773 
7774     // Scrolling
7775     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7776     {
7777         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7778         ImGuiWindow* window = g.NavWindow;
7779         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.
7780         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7781         {
7782             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7783                 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7784             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7785                 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7786         }
7787 
7788         // *Normal* Manual scroll with NavScrollXXX keys
7789         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7790         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
7791         if (scroll_dir.x != 0.0f && window->ScrollbarX)
7792         {
7793             SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
7794             g.NavMoveFromClampedRefRect = true;
7795         }
7796         if (scroll_dir.y != 0.0f)
7797         {
7798             SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
7799             g.NavMoveFromClampedRefRect = true;
7800         }
7801     }
7802 
7803     // Reset search results
7804     g.NavMoveResultLocal.Clear();
7805     g.NavMoveResultLocalVisibleSet.Clear();
7806     g.NavMoveResultOther.Clear();
7807 
7808     // 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
7809     if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7810     {
7811         ImGuiWindow* window = g.NavWindow;
7812         ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7813         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
7814         {
7815             float pad = window->CalcFontSize() * 0.5f;
7816             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
7817             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
7818             g.NavId = 0;
7819         }
7820         g.NavMoveFromClampedRefRect = false;
7821     }
7822 
7823     // 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)
7824     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7825     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7826     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
7827     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
7828     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7829     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().
7830     //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7831     g.NavScoringCount = 0;
7832 #if IMGUI_DEBUG_NAV_RECTS
7833     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]
7834     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); }
7835 #endif
7836 }
7837 
7838 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()7839 static void ImGui::NavUpdateMoveResult()
7840 {
7841     ImGuiContext& g = *GImGui;
7842     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7843     {
7844         // 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)
7845         if (g.NavId != 0)
7846         {
7847             g.NavDisableHighlight = false;
7848             g.NavDisableMouseHover = true;
7849         }
7850         return;
7851     }
7852 
7853     // Select which result to use
7854     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7855 
7856     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7857     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7858         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7859             result = &g.NavMoveResultLocalVisibleSet;
7860 
7861     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7862     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7863         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7864             result = &g.NavMoveResultOther;
7865     IM_ASSERT(g.NavWindow && result->Window);
7866 
7867     // Scroll to keep newly navigated item fully into view.
7868     if (g.NavLayer == 0)
7869     {
7870         ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7871         NavScrollToBringItemIntoView(result->Window, rect_abs);
7872 
7873         // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7874         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false);
7875         ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7876         result->RectRel.Translate(delta_scroll);
7877 
7878         // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7879         if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7880             NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7881     }
7882 
7883     ClearActiveID();
7884     g.NavWindow = result->Window;
7885     if (g.NavId != result->ID)
7886     {
7887         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
7888         g.NavJustMovedToId = result->ID;
7889         g.NavJustMovedToSelectScopeId = result->SelectScopeId;
7890     }
7891     SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
7892     g.NavMoveFromClampedRefRect = false;
7893 }
7894 
NavUpdatePageUpPageDown(int allowed_dir_flags)7895 static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7896 {
7897     ImGuiContext& g = *GImGui;
7898     if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7899     {
7900         ImGuiWindow* window = g.NavWindow;
7901         bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7902         bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7903         if (page_up_held != page_down_held) // If either (not both) are pressed
7904         {
7905             if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7906             {
7907                 // Fallback manual-scroll when window has no navigable item
7908                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7909                     SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
7910                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7911                     SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
7912             }
7913             else
7914             {
7915                 const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7916                 const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7917                 float nav_scoring_rect_offset_y = 0.0f;
7918                 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7919                 {
7920                     nav_scoring_rect_offset_y = -page_offset_y;
7921                     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)
7922                     g.NavMoveClipDir = ImGuiDir_Up;
7923                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7924                 }
7925                 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7926                 {
7927                     nav_scoring_rect_offset_y = +page_offset_y;
7928                     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)
7929                     g.NavMoveClipDir = ImGuiDir_Down;
7930                     g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7931                 }
7932                 return nav_scoring_rect_offset_y;
7933             }
7934         }
7935     }
7936     return 0.0f;
7937 }
7938 
FindWindowFocusIndex(ImGuiWindow * window)7939 static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7940 {
7941     ImGuiContext& g = *GImGui;
7942     for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
7943         if (g.WindowsFocusOrder[i] == window)
7944             return i;
7945     return -1;
7946 }
7947 
FindWindowNavFocusable(int i_start,int i_stop,int dir)7948 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7949 {
7950     ImGuiContext& g = *GImGui;
7951     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
7952         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
7953             return g.WindowsFocusOrder[i];
7954     return NULL;
7955 }
7956 
NavUpdateWindowingHighlightWindow(int focus_change_dir)7957 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7958 {
7959     ImGuiContext& g = *GImGui;
7960     IM_ASSERT(g.NavWindowingTarget);
7961     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7962         return;
7963 
7964     const int i_current = FindWindowFocusIndex(g.NavWindowingTarget);
7965     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
7966     if (!window_target)
7967         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
7968     if (window_target) // Don't reset windowing target if there's a single window in the list
7969         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7970     g.NavWindowingToggleLayer = false;
7971 }
7972 
7973 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()7974 static void ImGui::NavUpdateWindowing()
7975 {
7976     ImGuiContext& g = *GImGui;
7977     ImGuiWindow* apply_focus_window = NULL;
7978     bool apply_toggle_layer = false;
7979 
7980     ImGuiWindow* modal_window = GetFrontMostPopupModal();
7981     if (modal_window != NULL)
7982     {
7983         g.NavWindowingTarget = NULL;
7984         return;
7985     }
7986 
7987     // Fade out
7988     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7989     {
7990         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
7991         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7992             g.NavWindowingTargetAnim = NULL;
7993     }
7994 
7995     // Start CTRL-TAB or Square+L/R window selection
7996     bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
7997     bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7998     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7999         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8000         {
8001             g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
8002             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8003             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8004             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8005         }
8006 
8007     // Gamepad update
8008     g.NavWindowingTimer += g.IO.DeltaTime;
8009     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8010     {
8011         // 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
8012         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8013 
8014         // Select window to focus
8015         const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8016         if (focus_change_dir != 0)
8017         {
8018             NavUpdateWindowingHighlightWindow(focus_change_dir);
8019             g.NavWindowingHighlightAlpha = 1.0f;
8020         }
8021 
8022         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
8023         if (!IsNavInputDown(ImGuiNavInput_Menu))
8024         {
8025             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8026             if (g.NavWindowingToggleLayer && g.NavWindow)
8027                 apply_toggle_layer = true;
8028             else if (!g.NavWindowingToggleLayer)
8029                 apply_focus_window = g.NavWindowingTarget;
8030             g.NavWindowingTarget = NULL;
8031         }
8032     }
8033 
8034     // Keyboard: Focus
8035     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8036     {
8037         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8038         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8039         if (IsKeyPressedMap(ImGuiKey_Tab, true))
8040             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8041         if (!g.IO.KeyCtrl)
8042             apply_focus_window = g.NavWindowingTarget;
8043     }
8044 
8045     // Keyboard: Press and Release ALT to toggle menu layer
8046     // 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
8047     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8048         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8049             apply_toggle_layer = true;
8050 
8051     // Move window
8052     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8053     {
8054         ImVec2 move_delta;
8055         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8056             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8057         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8058             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8059         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8060         {
8061             const float NAV_MOVE_SPEED = 800.0f;
8062             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
8063             g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
8064             g.NavDisableMouseHover = true;
8065             MarkIniSettingsDirty(g.NavWindowingTarget);
8066         }
8067     }
8068 
8069     // Apply final focus
8070     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8071     {
8072         g.NavDisableHighlight = false;
8073         g.NavDisableMouseHover = true;
8074         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8075         ClosePopupsOverWindow(apply_focus_window);
8076         FocusWindow(apply_focus_window);
8077         if (apply_focus_window->NavLastIds[0] == 0)
8078             NavInitWindow(apply_focus_window, false);
8079 
8080         // If the window only has a menu layer, select it directly
8081         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8082             g.NavLayer = ImGuiNavLayer_Menu;
8083     }
8084     if (apply_focus_window)
8085         g.NavWindowingTarget = NULL;
8086 
8087     // Apply menu/layer toggle
8088     if (apply_toggle_layer && g.NavWindow)
8089     {
8090         // Move to parent menu if necessary
8091         ImGuiWindow* new_nav_window = g.NavWindow;
8092         while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0
8093             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8094             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8095             new_nav_window = new_nav_window->ParentWindow;
8096         if (new_nav_window != g.NavWindow)
8097         {
8098             ImGuiWindow* old_nav_window = g.NavWindow;
8099             FocusWindow(new_nav_window);
8100             new_nav_window->NavLastChildNavWindow = old_nav_window;
8101         }
8102         g.NavDisableHighlight = false;
8103         g.NavDisableMouseHover = true;
8104         NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main);
8105     }
8106 }
8107 
8108 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)8109 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8110 {
8111     if (window->Flags & ImGuiWindowFlags_Popup)
8112         return "(Popup)";
8113     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8114         return "(Main menu bar)";
8115     return "(Untitled)";
8116 }
8117 
8118 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingList()8119 void ImGui::NavUpdateWindowingList()
8120 {
8121     ImGuiContext& g = *GImGui;
8122     IM_ASSERT(g.NavWindowingTarget != NULL);
8123 
8124     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8125         return;
8126 
8127     if (g.NavWindowingList == NULL)
8128         g.NavWindowingList = FindWindowByName("###NavWindowingList");
8129     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8130     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8131     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8132     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8133     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8134     {
8135         ImGuiWindow* window = g.WindowsFocusOrder[n];
8136         if (!IsWindowNavFocusable(window))
8137             continue;
8138         const char* label = window->Name;
8139         if (label == FindRenderedTextEnd(label))
8140             label = GetFallbackWindowNameForWindowingList(window);
8141         Selectable(label, g.NavWindowingTarget == window);
8142     }
8143     End();
8144     PopStyleVar();
8145 }
8146 
8147 //-----------------------------------------------------------------------------
8148 // [SECTION] COLUMNS
8149 // In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
8150 //-----------------------------------------------------------------------------
8151 
NextColumn()8152 void ImGui::NextColumn()
8153 {
8154     ImGuiWindow* window = GetCurrentWindow();
8155     if (window->SkipItems || window->DC.ColumnsSet == NULL)
8156         return;
8157 
8158     ImGuiContext& g = *GImGui;
8159     PopItemWidth();
8160     PopClipRect();
8161 
8162     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8163     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8164     if (++columns->Current < columns->Count)
8165     {
8166         // Columns 1+ cancel out IndentX
8167         window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
8168         window->DrawList->ChannelsSetCurrent(columns->Current);
8169     }
8170     else
8171     {
8172         window->DC.ColumnsOffset.x = 0.0f;
8173         window->DrawList->ChannelsSetCurrent(0);
8174         columns->Current = 0;
8175         columns->LineMinY = columns->LineMaxY;
8176     }
8177     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8178     window->DC.CursorPos.y = columns->LineMinY;
8179     window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
8180     window->DC.CurrentLineTextBaseOffset = 0.0f;
8181 
8182     PushColumnClipRect();
8183     PushItemWidth(GetColumnWidth() * 0.65f);  // FIXME: Move on columns setup
8184 }
8185 
GetColumnIndex()8186 int ImGui::GetColumnIndex()
8187 {
8188     ImGuiWindow* window = GetCurrentWindowRead();
8189     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
8190 }
8191 
GetColumnsCount()8192 int ImGui::GetColumnsCount()
8193 {
8194     ImGuiWindow* window = GetCurrentWindowRead();
8195     return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
8196 }
8197 
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)8198 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
8199 {
8200     return offset_norm * (columns->MaxX - columns->MinX);
8201 }
8202 
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)8203 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
8204 {
8205     return offset / (columns->MaxX - columns->MinX);
8206 }
8207 
GetColumnsRectHalfWidth()8208 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
8209 
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)8210 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
8211 {
8212     // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
8213     // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
8214     ImGuiContext& g = *GImGui;
8215     ImGuiWindow* window = g.CurrentWindow;
8216     IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
8217     IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
8218 
8219     float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
8220     x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
8221     if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
8222         x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
8223 
8224     return x;
8225 }
8226 
GetColumnOffset(int column_index)8227 float ImGui::GetColumnOffset(int column_index)
8228 {
8229     ImGuiWindow* window = GetCurrentWindowRead();
8230     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8231     IM_ASSERT(columns != NULL);
8232 
8233     if (column_index < 0)
8234         column_index = columns->Current;
8235     IM_ASSERT(column_index < columns->Columns.Size);
8236 
8237     const float t = columns->Columns[column_index].OffsetNorm;
8238     const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
8239     return x_offset;
8240 }
8241 
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)8242 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
8243 {
8244     if (column_index < 0)
8245         column_index = columns->Current;
8246 
8247     float offset_norm;
8248     if (before_resize)
8249         offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
8250     else
8251         offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
8252     return OffsetNormToPixels(columns, offset_norm);
8253 }
8254 
GetColumnWidth(int column_index)8255 float ImGui::GetColumnWidth(int column_index)
8256 {
8257     ImGuiWindow* window = GetCurrentWindowRead();
8258     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8259     IM_ASSERT(columns != NULL);
8260 
8261     if (column_index < 0)
8262         column_index = columns->Current;
8263     return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
8264 }
8265 
SetColumnOffset(int column_index,float offset)8266 void ImGui::SetColumnOffset(int column_index, float offset)
8267 {
8268     ImGuiContext& g = *GImGui;
8269     ImGuiWindow* window = g.CurrentWindow;
8270     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8271     IM_ASSERT(columns != NULL);
8272 
8273     if (column_index < 0)
8274         column_index = columns->Current;
8275     IM_ASSERT(column_index < columns->Columns.Size);
8276 
8277     const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
8278     const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
8279 
8280     if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
8281         offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
8282     columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
8283 
8284     if (preserve_width)
8285         SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
8286 }
8287 
SetColumnWidth(int column_index,float width)8288 void ImGui::SetColumnWidth(int column_index, float width)
8289 {
8290     ImGuiWindow* window = GetCurrentWindowRead();
8291     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8292     IM_ASSERT(columns != NULL);
8293 
8294     if (column_index < 0)
8295         column_index = columns->Current;
8296     SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
8297 }
8298 
PushColumnClipRect(int column_index)8299 void ImGui::PushColumnClipRect(int column_index)
8300 {
8301     ImGuiWindow* window = GetCurrentWindowRead();
8302     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8303     if (column_index < 0)
8304         column_index = columns->Current;
8305 
8306     PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
8307 }
8308 
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)8309 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
8310 {
8311     for (int n = 0; n < window->ColumnsStorage.Size; n++)
8312         if (window->ColumnsStorage[n].ID == id)
8313             return &window->ColumnsStorage[n];
8314 
8315     window->ColumnsStorage.push_back(ImGuiColumnsSet());
8316     ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
8317     columns->ID = id;
8318     return columns;
8319 }
8320 
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)8321 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
8322 {
8323     ImGuiContext& g = *GImGui;
8324     ImGuiWindow* window = GetCurrentWindow();
8325 
8326     IM_ASSERT(columns_count > 1);
8327     IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
8328 
8329     // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
8330     // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
8331     PushID(0x11223347 + (str_id ? 0 : columns_count));
8332     ImGuiID id = window->GetID(str_id ? str_id : "columns");
8333     PopID();
8334 
8335     // Acquire storage for the columns set
8336     ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
8337     IM_ASSERT(columns->ID == id);
8338     columns->Current = 0;
8339     columns->Count = columns_count;
8340     columns->Flags = flags;
8341     window->DC.ColumnsSet = columns;
8342 
8343     // Set state for first column
8344     const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
8345     columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
8346     columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
8347     columns->StartPosY = window->DC.CursorPos.y;
8348     columns->StartMaxPosX = window->DC.CursorMaxPos.x;
8349     columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
8350     window->DC.ColumnsOffset.x = 0.0f;
8351     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8352 
8353     // Clear data if columns count changed
8354     if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
8355         columns->Columns.resize(0);
8356 
8357     // Initialize defaults
8358     columns->IsFirstFrame = (columns->Columns.Size == 0);
8359     if (columns->Columns.Size == 0)
8360     {
8361         columns->Columns.reserve(columns_count + 1);
8362         for (int n = 0; n < columns_count + 1; n++)
8363         {
8364             ImGuiColumnData column;
8365             column.OffsetNorm = n / (float)columns_count;
8366             columns->Columns.push_back(column);
8367         }
8368     }
8369 
8370     for (int n = 0; n < columns_count; n++)
8371     {
8372         // Compute clipping rectangle
8373         ImGuiColumnData* column = &columns->Columns[n];
8374         float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
8375         float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
8376         column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
8377         column->ClipRect.ClipWith(window->ClipRect);
8378     }
8379 
8380     window->DrawList->ChannelsSplit(columns->Count);
8381     PushColumnClipRect();
8382     PushItemWidth(GetColumnWidth() * 0.65f);
8383 }
8384 
EndColumns()8385 void ImGui::EndColumns()
8386 {
8387     ImGuiContext& g = *GImGui;
8388     ImGuiWindow* window = GetCurrentWindow();
8389     ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8390     IM_ASSERT(columns != NULL);
8391 
8392     PopItemWidth();
8393     PopClipRect();
8394     window->DrawList->ChannelsMerge();
8395 
8396     columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8397     window->DC.CursorPos.y = columns->LineMaxY;
8398     if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
8399         window->DC.CursorMaxPos.x = columns->StartMaxPosX;  // Restore cursor max pos, as columns don't grow parent
8400 
8401     // Draw columns borders and handle resize
8402     bool is_being_resized = false;
8403     if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
8404     {
8405         const float y1 = columns->StartPosY;
8406         const float y2 = window->DC.CursorPos.y;
8407         int dragging_column = -1;
8408         for (int n = 1; n < columns->Count; n++)
8409         {
8410             float x = window->Pos.x + GetColumnOffset(n);
8411             const ImGuiID column_id = columns->ID + ImGuiID(n);
8412             const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
8413             const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
8414             KeepAliveID(column_id);
8415             if (IsClippedEx(column_rect, column_id, false))
8416                 continue;
8417 
8418             bool hovered = false, held = false;
8419             if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
8420             {
8421                 ButtonBehavior(column_rect, column_id, &hovered, &held);
8422                 if (hovered || held)
8423                     g.MouseCursor = ImGuiMouseCursor_ResizeEW;
8424                 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
8425                     dragging_column = n;
8426             }
8427 
8428             // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
8429             const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
8430             const float xi = (float)(int)x;
8431             window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
8432         }
8433 
8434         // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
8435         if (dragging_column != -1)
8436         {
8437             if (!columns->IsBeingResized)
8438                 for (int n = 0; n < columns->Count + 1; n++)
8439                     columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
8440             columns->IsBeingResized = is_being_resized = true;
8441             float x = GetDraggedColumnOffset(columns, dragging_column);
8442             SetColumnOffset(dragging_column, x);
8443         }
8444     }
8445     columns->IsBeingResized = is_being_resized;
8446 
8447     window->DC.ColumnsSet = NULL;
8448     window->DC.ColumnsOffset.x = 0.0f;
8449     window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8450 }
8451 
8452 // [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)8453 void ImGui::Columns(int columns_count, const char* id, bool border)
8454 {
8455     ImGuiWindow* window = GetCurrentWindow();
8456     IM_ASSERT(columns_count >= 1);
8457 
8458     ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
8459     //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
8460     if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
8461         return;
8462 
8463     if (window->DC.ColumnsSet != NULL)
8464         EndColumns();
8465 
8466     if (columns_count != 1)
8467         BeginColumns(id, columns_count, flags);
8468 }
8469 
8470 //-----------------------------------------------------------------------------
8471 // [SECTION] DRAG AND DROP
8472 //-----------------------------------------------------------------------------
8473 
ClearDragDrop()8474 void ImGui::ClearDragDrop()
8475 {
8476     ImGuiContext& g = *GImGui;
8477     g.DragDropActive = false;
8478     g.DragDropPayload.Clear();
8479     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8480     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8481     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8482     g.DragDropAcceptFrameCount = -1;
8483 
8484     g.DragDropPayloadBufHeap.clear();
8485     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8486 }
8487 
8488 // Call when current ID is active.
8489 // 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)8490 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8491 {
8492     ImGuiContext& g = *GImGui;
8493     ImGuiWindow* window = g.CurrentWindow;
8494 
8495     bool source_drag_active = false;
8496     ImGuiID source_id = 0;
8497     ImGuiID source_parent_id = 0;
8498     int mouse_button = 0;
8499     if (!(flags & ImGuiDragDropFlags_SourceExtern))
8500     {
8501         source_id = window->DC.LastItemId;
8502         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8503             return false;
8504         if (g.IO.MouseDown[mouse_button] == false)
8505             return false;
8506 
8507         if (source_id == 0)
8508         {
8509             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8510             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8511             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8512             {
8513                 IM_ASSERT(0);
8514                 return false;
8515             }
8516 
8517             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8518             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8519             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8520             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8521             bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8522             if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8523                 return false;
8524             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8525             if (is_hovered)
8526                 SetHoveredID(source_id);
8527             if (is_hovered && g.IO.MouseClicked[mouse_button])
8528             {
8529                 SetActiveID(source_id, window);
8530                 FocusWindow(window);
8531             }
8532             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8533                 g.ActiveIdAllowOverlap = is_hovered;
8534         }
8535         else
8536         {
8537             g.ActiveIdAllowOverlap = false;
8538         }
8539         if (g.ActiveId != source_id)
8540             return false;
8541         source_parent_id = window->IDStack.back();
8542         source_drag_active = IsMouseDragging(mouse_button);
8543     }
8544     else
8545     {
8546         window = NULL;
8547         source_id = ImHashStr("#SourceExtern", 0);
8548         source_drag_active = true;
8549     }
8550 
8551     if (source_drag_active)
8552     {
8553         if (!g.DragDropActive)
8554         {
8555             IM_ASSERT(source_id != 0);
8556             ClearDragDrop();
8557             ImGuiPayload& payload = g.DragDropPayload;
8558             payload.SourceId = source_id;
8559             payload.SourceParentId = source_parent_id;
8560             g.DragDropActive = true;
8561             g.DragDropSourceFlags = flags;
8562             g.DragDropMouseButton = mouse_button;
8563         }
8564         g.DragDropSourceFrameCount = g.FrameCount;
8565         g.DragDropWithinSourceOrTarget = true;
8566 
8567         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8568         {
8569             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8570             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8571             BeginTooltip();
8572             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8573             {
8574                 ImGuiWindow* tooltip_window = g.CurrentWindow;
8575                 tooltip_window->SkipItems = true;
8576                 tooltip_window->HiddenFramesRegular = 1;
8577             }
8578         }
8579 
8580         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8581             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8582 
8583         return true;
8584     }
8585     return false;
8586 }
8587 
EndDragDropSource()8588 void ImGui::EndDragDropSource()
8589 {
8590     ImGuiContext& g = *GImGui;
8591     IM_ASSERT(g.DragDropActive);
8592     IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8593 
8594     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8595         EndTooltip();
8596 
8597     // Discard the drag if have not called SetDragDropPayload()
8598     if (g.DragDropPayload.DataFrameCount == -1)
8599         ClearDragDrop();
8600     g.DragDropWithinSourceOrTarget = false;
8601 }
8602 
8603 // 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)8604 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8605 {
8606     ImGuiContext& g = *GImGui;
8607     ImGuiPayload& payload = g.DragDropPayload;
8608     if (cond == 0)
8609         cond = ImGuiCond_Always;
8610 
8611     IM_ASSERT(type != NULL);
8612     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8613     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8614     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8615     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
8616 
8617     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8618     {
8619         // Copy payload
8620         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8621         g.DragDropPayloadBufHeap.resize(0);
8622         if (data_size > sizeof(g.DragDropPayloadBufLocal))
8623         {
8624             // Store in heap
8625             g.DragDropPayloadBufHeap.resize((int)data_size);
8626             payload.Data = g.DragDropPayloadBufHeap.Data;
8627             memcpy(payload.Data, data, data_size);
8628         }
8629         else if (data_size > 0)
8630         {
8631             // Store locally
8632             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8633             payload.Data = g.DragDropPayloadBufLocal;
8634             memcpy(payload.Data, data, data_size);
8635         }
8636         else
8637         {
8638             payload.Data = NULL;
8639         }
8640         payload.DataSize = (int)data_size;
8641     }
8642     payload.DataFrameCount = g.FrameCount;
8643 
8644     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8645 }
8646 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8647 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8648 {
8649     ImGuiContext& g = *GImGui;
8650     if (!g.DragDropActive)
8651         return false;
8652 
8653     ImGuiWindow* window = g.CurrentWindow;
8654     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8655         return false;
8656     IM_ASSERT(id != 0);
8657     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8658         return false;
8659     if (window->SkipItems)
8660         return false;
8661 
8662     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8663     g.DragDropTargetRect = bb;
8664     g.DragDropTargetId = id;
8665     g.DragDropWithinSourceOrTarget = true;
8666     return true;
8667 }
8668 
8669 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8670 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8671 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8672 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8673 bool ImGui::BeginDragDropTarget()
8674 {
8675     ImGuiContext& g = *GImGui;
8676     if (!g.DragDropActive)
8677         return false;
8678 
8679     ImGuiWindow* window = g.CurrentWindow;
8680     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8681         return false;
8682     if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8683         return false;
8684 
8685     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8686     ImGuiID id = window->DC.LastItemId;
8687     if (id == 0)
8688         id = window->GetIDFromRectangle(display_rect);
8689     if (g.DragDropPayload.SourceId == id)
8690         return false;
8691 
8692     IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8693     g.DragDropTargetRect = display_rect;
8694     g.DragDropTargetId = id;
8695     g.DragDropWithinSourceOrTarget = true;
8696     return true;
8697 }
8698 
IsDragDropPayloadBeingAccepted()8699 bool ImGui::IsDragDropPayloadBeingAccepted()
8700 {
8701     ImGuiContext& g = *GImGui;
8702     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8703 }
8704 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8705 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8706 {
8707     ImGuiContext& g = *GImGui;
8708     ImGuiWindow* window = g.CurrentWindow;
8709     ImGuiPayload& payload = g.DragDropPayload;
8710     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8711     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
8712     if (type != NULL && !payload.IsDataType(type))
8713         return NULL;
8714 
8715     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8716     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8717     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8718     ImRect r = g.DragDropTargetRect;
8719     float r_surface = r.GetWidth() * r.GetHeight();
8720     if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8721     {
8722         g.DragDropAcceptFlags = flags;
8723         g.DragDropAcceptIdCurr = g.DragDropTargetId;
8724         g.DragDropAcceptIdCurrRectSurface = r_surface;
8725     }
8726 
8727     // Render default drop visuals
8728     payload.Preview = was_accepted_previously;
8729     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8730     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8731     {
8732         // FIXME-DRAG: Settle on a proper default visuals for drop target.
8733         r.Expand(3.5f);
8734         bool push_clip_rect = !window->ClipRect.Contains(r);
8735         if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8736         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8737         if (push_clip_rect) window->DrawList->PopClipRect();
8738     }
8739 
8740     g.DragDropAcceptFrameCount = g.FrameCount;
8741     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()
8742     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8743         return NULL;
8744 
8745     return &payload;
8746 }
8747 
GetDragDropPayload()8748 const ImGuiPayload* ImGui::GetDragDropPayload()
8749 {
8750     ImGuiContext& g = *GImGui;
8751     return g.DragDropActive ? &g.DragDropPayload : NULL;
8752 }
8753 
8754 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8755 void ImGui::EndDragDropTarget()
8756 {
8757     ImGuiContext& g = *GImGui;
8758     IM_ASSERT(g.DragDropActive);
8759     IM_ASSERT(g.DragDropWithinSourceOrTarget);
8760     g.DragDropWithinSourceOrTarget = false;
8761 }
8762 
8763 //-----------------------------------------------------------------------------
8764 // [SECTION] DOCKING
8765 //-----------------------------------------------------------------------------
8766 
8767 // (this section is filled in the 'docking' branch)
8768 
8769 //-----------------------------------------------------------------------------
8770 // [SECTION] LOGGING/CAPTURING
8771 //-----------------------------------------------------------------------------
8772 // All text output from the interface can be captured into tty/file/clipboard.
8773 // By default, tree nodes are automatically opened during logging.
8774 //-----------------------------------------------------------------------------
8775 
8776 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8777 void ImGui::LogText(const char* fmt, ...)
8778 {
8779     ImGuiContext& g = *GImGui;
8780     if (!g.LogEnabled)
8781         return;
8782 
8783     va_list args;
8784     va_start(args, fmt);
8785     if (g.LogFile)
8786         vfprintf(g.LogFile, fmt, args);
8787     else
8788         g.LogClipboard.appendfv(fmt, args);
8789     va_end(args);
8790 }
8791 
8792 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
8793 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)8794 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8795 {
8796     ImGuiContext& g = *GImGui;
8797     ImGuiWindow* window = g.CurrentWindow;
8798 
8799     if (!text_end)
8800         text_end = FindRenderedTextEnd(text, text_end);
8801 
8802     const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8803     if (ref_pos)
8804         window->DC.LogLinePosY = ref_pos->y;
8805 
8806     const char* text_remaining = text;
8807     if (g.LogStartDepth > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
8808         g.LogStartDepth = window->DC.TreeDepth;
8809     const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8810     for (;;)
8811     {
8812         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8813         const char* line_start = text_remaining;
8814         const char* line_end = ImStreolRange(line_start, text_end);
8815         const bool is_first_line = (line_start == text);
8816         const bool is_last_line = (line_end == text_end);
8817         if (!is_last_line || (line_start != line_end))
8818         {
8819             const int char_count = (int)(line_end - line_start);
8820             if (log_new_line || !is_first_line)
8821                 LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start);
8822             else
8823                 LogText(" %.*s", char_count, line_start);
8824         }
8825 
8826         if (is_last_line)
8827             break;
8828         text_remaining = line_end + 1;
8829     }
8830 }
8831 
8832 // Start logging ImGui output to TTY
LogToTTY(int max_depth)8833 void ImGui::LogToTTY(int max_depth)
8834 {
8835     ImGuiContext& g = *GImGui;
8836     if (g.LogEnabled)
8837         return;
8838     ImGuiWindow* window = g.CurrentWindow;
8839 
8840     IM_ASSERT(g.LogFile == NULL);
8841     g.LogFile = stdout;
8842     g.LogEnabled = true;
8843     g.LogStartDepth = window->DC.TreeDepth;
8844     if (max_depth >= 0)
8845         g.LogAutoExpandMaxDepth = max_depth;
8846 }
8847 
8848 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)8849 void ImGui::LogToFile(int max_depth, const char* filename)
8850 {
8851     ImGuiContext& g = *GImGui;
8852     if (g.LogEnabled)
8853         return;
8854     ImGuiWindow* window = g.CurrentWindow;
8855 
8856     if (!filename)
8857     {
8858         filename = g.IO.LogFilename;
8859         if (!filename)
8860             return;
8861     }
8862 
8863     IM_ASSERT(g.LogFile == NULL);
8864     g.LogFile = ImFileOpen(filename, "ab");
8865     if (!g.LogFile)
8866     {
8867         IM_ASSERT(0);
8868         return;
8869     }
8870     g.LogEnabled = true;
8871     g.LogStartDepth = window->DC.TreeDepth;
8872     if (max_depth >= 0)
8873         g.LogAutoExpandMaxDepth = max_depth;
8874 }
8875 
8876 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)8877 void ImGui::LogToClipboard(int max_depth)
8878 {
8879     ImGuiContext& g = *GImGui;
8880     if (g.LogEnabled)
8881         return;
8882     ImGuiWindow* window = g.CurrentWindow;
8883 
8884     IM_ASSERT(g.LogFile == NULL);
8885     g.LogFile = NULL;
8886     g.LogEnabled = true;
8887     g.LogStartDepth = window->DC.TreeDepth;
8888     if (max_depth >= 0)
8889         g.LogAutoExpandMaxDepth = max_depth;
8890 }
8891 
LogFinish()8892 void ImGui::LogFinish()
8893 {
8894     ImGuiContext& g = *GImGui;
8895     if (!g.LogEnabled)
8896         return;
8897 
8898     LogText(IM_NEWLINE);
8899     if (g.LogFile != NULL)
8900     {
8901         if (g.LogFile == stdout)
8902             fflush(g.LogFile);
8903         else
8904             fclose(g.LogFile);
8905         g.LogFile = NULL;
8906     }
8907     if (g.LogClipboard.size() > 1)
8908     {
8909         SetClipboardText(g.LogClipboard.begin());
8910         g.LogClipboard.clear();
8911     }
8912     g.LogEnabled = false;
8913 }
8914 
8915 // Helper to display logging buttons
LogButtons()8916 void ImGui::LogButtons()
8917 {
8918     ImGuiContext& g = *GImGui;
8919 
8920     PushID("LogButtons");
8921     const bool log_to_tty = Button("Log To TTY"); SameLine();
8922     const bool log_to_file = Button("Log To File"); SameLine();
8923     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
8924     PushItemWidth(80.0f);
8925     PushAllowKeyboardFocus(false);
8926     SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8927     PopAllowKeyboardFocus();
8928     PopItemWidth();
8929     PopID();
8930 
8931     // Start logging at the end of the function so that the buttons don't appear in the log
8932     if (log_to_tty)
8933         LogToTTY(g.LogAutoExpandMaxDepth);
8934     if (log_to_file)
8935         LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8936     if (log_to_clipboard)
8937         LogToClipboard(g.LogAutoExpandMaxDepth);
8938 }
8939 
8940 //-----------------------------------------------------------------------------
8941 // [SECTION] SETTINGS
8942 //-----------------------------------------------------------------------------
8943 
MarkIniSettingsDirty()8944 void ImGui::MarkIniSettingsDirty()
8945 {
8946     ImGuiContext& g = *GImGui;
8947     if (g.SettingsDirtyTimer <= 0.0f)
8948         g.SettingsDirtyTimer = g.IO.IniSavingRate;
8949 }
8950 
MarkIniSettingsDirty(ImGuiWindow * window)8951 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8952 {
8953     ImGuiContext& g = *GImGui;
8954     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8955         if (g.SettingsDirtyTimer <= 0.0f)
8956             g.SettingsDirtyTimer = g.IO.IniSavingRate;
8957 }
8958 
CreateNewWindowSettings(const char * name)8959 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
8960 {
8961     ImGuiContext& g = *GImGui;
8962     g.SettingsWindows.push_back(ImGuiWindowSettings());
8963     ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8964     settings->Name = ImStrdup(name);
8965     settings->ID = ImHashStr(name, 0);
8966     return settings;
8967 }
8968 
FindWindowSettings(ImGuiID id)8969 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8970 {
8971     ImGuiContext& g = *GImGui;
8972     for (int i = 0; i != g.SettingsWindows.Size; i++)
8973         if (g.SettingsWindows[i].ID == id)
8974             return &g.SettingsWindows[i];
8975     return NULL;
8976 }
8977 
FindOrCreateWindowSettings(const char * name)8978 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
8979 {
8980     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name, 0)))
8981         return settings;
8982     return CreateNewWindowSettings(name);
8983 }
8984 
LoadIniSettingsFromDisk(const char * ini_filename)8985 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8986 {
8987     size_t file_data_size = 0;
8988     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
8989     if (!file_data)
8990         return;
8991     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
8992     ImGui::MemFree(file_data);
8993 }
8994 
FindSettingsHandler(const char * type_name)8995 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8996 {
8997     ImGuiContext& g = *GImGui;
8998     const ImGuiID type_hash = ImHashStr(type_name, 0);
8999     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9000         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9001             return &g.SettingsHandlers[handler_n];
9002     return NULL;
9003 }
9004 
9005 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9006 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9007 {
9008     ImGuiContext& g = *GImGui;
9009     IM_ASSERT(g.Initialized);
9010     IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9011 
9012     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9013     // 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..
9014     if (ini_size == 0)
9015         ini_size = strlen(ini_data);
9016     char* buf = (char*)ImGui::MemAlloc(ini_size + 1);
9017     char* buf_end = buf + ini_size;
9018     memcpy(buf, ini_data, ini_size);
9019     buf[ini_size] = 0;
9020 
9021     void* entry_data = NULL;
9022     ImGuiSettingsHandler* entry_handler = NULL;
9023 
9024     char* line_end = NULL;
9025     for (char* line = buf; line < buf_end; line = line_end + 1)
9026     {
9027         // Skip new lines markers, then find end of the line
9028         while (*line == '\n' || *line == '\r')
9029             line++;
9030         line_end = line;
9031         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9032             line_end++;
9033         line_end[0] = 0;
9034         if (line[0] == ';')
9035             continue;
9036         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9037         {
9038             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9039             line_end[-1] = 0;
9040             const char* name_end = line_end - 1;
9041             const char* type_start = line + 1;
9042             char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
9043             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9044             if (!type_end || !name_start)
9045             {
9046                 name_start = type_start; // Import legacy entries that have no type
9047                 type_start = "Window";
9048             }
9049             else
9050             {
9051                 *type_end = 0; // Overwrite first ']'
9052                 name_start++;  // Skip second '['
9053             }
9054             entry_handler = FindSettingsHandler(type_start);
9055             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9056         }
9057         else if (entry_handler != NULL && entry_data != NULL)
9058         {
9059             // Let type handler parse the line
9060             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9061         }
9062     }
9063     ImGui::MemFree(buf);
9064     g.SettingsLoaded = true;
9065 }
9066 
SaveIniSettingsToDisk(const char * ini_filename)9067 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9068 {
9069     ImGuiContext& g = *GImGui;
9070     g.SettingsDirtyTimer = 0.0f;
9071     if (!ini_filename)
9072         return;
9073 
9074     size_t ini_data_size = 0;
9075     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9076     FILE* f = ImFileOpen(ini_filename, "wt");
9077     if (!f)
9078         return;
9079     fwrite(ini_data, sizeof(char), ini_data_size, f);
9080     fclose(f);
9081 }
9082 
9083 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9084 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9085 {
9086     ImGuiContext& g = *GImGui;
9087     g.SettingsDirtyTimer = 0.0f;
9088     g.SettingsIniData.Buf.resize(0);
9089     g.SettingsIniData.Buf.push_back(0);
9090     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9091     {
9092         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9093         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9094     }
9095     if (out_size)
9096         *out_size = (size_t)g.SettingsIniData.size();
9097     return g.SettingsIniData.c_str();
9098 }
9099 
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9100 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9101 {
9102     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name, 0));
9103     if (!settings)
9104         settings = ImGui::CreateNewWindowSettings(name);
9105     return (void*)settings;
9106 }
9107 
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)9108 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9109 {
9110     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9111     float x, y;
9112     int i;
9113     if (sscanf(line, "Pos=%f,%f", &x, &y) == 2)         settings->Pos = ImVec2(x, y);
9114     else if (sscanf(line, "Size=%f,%f", &x, &y) == 2)   settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
9115     else if (sscanf(line, "Collapsed=%d", &i) == 1)     settings->Collapsed = (i != 0);
9116 }
9117 
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)9118 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9119 {
9120     // Gather data from windows that were active during this session
9121     // (if a window wasn't opened in this session we preserve its settings)
9122     ImGuiContext& g = *imgui_ctx;
9123     for (int i = 0; i != g.Windows.Size; i++)
9124     {
9125         ImGuiWindow* window = g.Windows[i];
9126         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9127             continue;
9128 
9129         ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
9130         if (!settings)
9131         {
9132             settings = ImGui::CreateNewWindowSettings(window->Name);
9133             window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
9134         }
9135         IM_ASSERT(settings->ID == window->ID);
9136         settings->Pos = window->Pos;
9137         settings->Size = window->SizeFull;
9138         settings->Collapsed = window->Collapsed;
9139     }
9140 
9141     // Write to text buffer
9142     buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
9143     for (int i = 0; i != g.SettingsWindows.Size; i++)
9144     {
9145         const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
9146         if (settings->Pos.x == FLT_MAX)
9147             continue;
9148         const char* name = settings->Name;
9149         if (const char* p = strstr(name, "###"))  // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9150             name = p;
9151         buf->appendf("[%s][%s]\n", handler->TypeName, name);
9152         buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
9153         buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
9154         buf->appendf("Collapsed=%d\n", settings->Collapsed);
9155         buf->appendf("\n");
9156     }
9157 }
9158 
9159 //-----------------------------------------------------------------------------
9160 // [SECTION] PLATFORM DEPENDENT HELPERS
9161 //-----------------------------------------------------------------------------
9162 
9163 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9164 #ifndef WIN32_LEAN_AND_MEAN
9165 #define WIN32_LEAN_AND_MEAN
9166 #endif
9167 #ifndef __MINGW32__
9168 #include <Windows.h>
9169 #else
9170 #include <windows.h>
9171 #endif
9172 #endif
9173 
9174 // Win32 API clipboard implementation
9175 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9176 
9177 #ifdef _MSC_VER
9178 #pragma comment(lib, "user32")
9179 #endif
9180 
GetClipboardTextFn_DefaultImpl(void *)9181 static const char* GetClipboardTextFn_DefaultImpl(void*)
9182 {
9183     static ImVector<char> buf_local;
9184     buf_local.clear();
9185     if (!::OpenClipboard(NULL))
9186         return NULL;
9187     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9188     if (wbuf_handle == NULL)
9189     {
9190         ::CloseClipboard();
9191         return NULL;
9192     }
9193     if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9194     {
9195         int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9196         buf_local.resize(buf_len);
9197         ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9198     }
9199     ::GlobalUnlock(wbuf_handle);
9200     ::CloseClipboard();
9201     return buf_local.Data;
9202 }
9203 
SetClipboardTextFn_DefaultImpl(void *,const char * text)9204 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9205 {
9206     if (!::OpenClipboard(NULL))
9207         return;
9208     const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9209     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9210     if (wbuf_handle == NULL)
9211     {
9212         ::CloseClipboard();
9213         return;
9214     }
9215     ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9216     ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9217     ::GlobalUnlock(wbuf_handle);
9218     ::EmptyClipboard();
9219     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9220         ::GlobalFree(wbuf_handle);
9221     ::CloseClipboard();
9222 }
9223 
9224 #else
9225 
9226 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)9227 static const char* GetClipboardTextFn_DefaultImpl(void*)
9228 {
9229     ImGuiContext& g = *GImGui;
9230     return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9231 }
9232 
9233 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)9234 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9235 {
9236     ImGuiContext& g = *GImGui;
9237     g.PrivateClipboard.clear();
9238     const char* text_end = text + strlen(text);
9239     g.PrivateClipboard.resize((int)(text_end - text) + 1);
9240     memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9241     g.PrivateClipboard[(int)(text_end - text)] = 0;
9242 }
9243 
9244 #endif
9245 
9246 // Win32 API IME support (for Asian languages, etc.)
9247 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9248 
9249 #include <imm.h>
9250 #ifdef _MSC_VER
9251 #pragma comment(lib, "imm32")
9252 #endif
9253 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9254 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9255 {
9256     // Notify OS Input Method Editor of text input position
9257     if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9258         if (HIMC himc = ::ImmGetContext(hwnd))
9259         {
9260             COMPOSITIONFORM cf;
9261             cf.ptCurrentPos.x = x;
9262             cf.ptCurrentPos.y = y;
9263             cf.dwStyle = CFS_FORCE_POSITION;
9264             ::ImmSetCompositionWindow(himc, &cf);
9265             ::ImmReleaseContext(hwnd, himc);
9266         }
9267 }
9268 
9269 #else
9270 
ImeSetInputScreenPosFn_DefaultImpl(int,int)9271 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9272 
9273 #endif
9274 
9275 //-----------------------------------------------------------------------------
9276 // [SECTION] METRICS/DEBUG WINDOW
9277 //-----------------------------------------------------------------------------
9278 
ShowMetricsWindow(bool * p_open)9279 void ImGui::ShowMetricsWindow(bool* p_open)
9280 {
9281     if (!ImGui::Begin("ImGui Metrics", p_open))
9282     {
9283         ImGui::End();
9284         return;
9285     }
9286 
9287     static bool show_draw_cmd_clip_rects = true;
9288     static bool show_window_begin_order = false;
9289     ImGuiIO& io = ImGui::GetIO();
9290     ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9291     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9292     ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9293     ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9294     ImGui::Text("%d allocations", io.MetricsActiveAllocations);
9295     ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
9296     ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order);
9297     ImGui::Separator();
9298 
9299     struct Funcs
9300     {
9301         static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9302         {
9303             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);
9304             if (draw_list == ImGui::GetWindowDrawList())
9305             {
9306                 ImGui::SameLine();
9307                 ImGui::TextColored(ImVec4(1.0f,0.4f,0.4f,1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9308                 if (node_open) ImGui::TreePop();
9309                 return;
9310             }
9311 
9312             ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list
9313             if (window && IsItemHovered())
9314                 overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9315             if (!node_open)
9316                 return;
9317 
9318             int elem_offset = 0;
9319             for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9320             {
9321                 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9322                     continue;
9323                 if (pcmd->UserCallback)
9324                 {
9325                     ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9326                     continue;
9327                 }
9328                 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9329                 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);
9330                 if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
9331                 {
9332                     ImRect clip_rect = pcmd->ClipRect;
9333                     ImRect vtxs_rect;
9334                     for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9335                         vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9336                     clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
9337                     vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
9338                 }
9339                 if (!pcmd_node_open)
9340                     continue;
9341 
9342                 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9343                 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9344                 while (clipper.Step())
9345                     for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9346                     {
9347                         char buf[300];
9348                         char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9349                         ImVec2 triangles_pos[3];
9350                         for (int n = 0; n < 3; n++, idx_i++)
9351                         {
9352                             int vtx_i = idx_buffer ? idx_buffer[idx_i] : idx_i;
9353                             ImDrawVert& v = draw_list->VtxBuffer[vtx_i];
9354                             triangles_pos[n] = v.pos;
9355                             buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
9356                                 (n == 0) ? "idx" : "   ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9357                         }
9358                         ImGui::Selectable(buf, false);
9359                         if (ImGui::IsItemHovered())
9360                         {
9361                             ImDrawListFlags backup_flags = overlay_draw_list->Flags;
9362                             overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
9363                             overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
9364                             overlay_draw_list->Flags = backup_flags;
9365                         }
9366                     }
9367                 ImGui::TreePop();
9368             }
9369             ImGui::TreePop();
9370         }
9371 
9372         static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9373         {
9374             if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9375                 return;
9376             for (int i = 0; i < windows.Size; i++)
9377                 Funcs::NodeWindow(windows[i], "Window");
9378             ImGui::TreePop();
9379         }
9380 
9381         static void NodeWindow(ImGuiWindow* window, const char* label)
9382         {
9383             if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9384                 return;
9385             ImGuiWindowFlags flags = window->Flags;
9386             NodeDrawList(window, window->DrawList, "DrawList");
9387             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);
9388             ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9389                 (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9390                 (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9391                 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9392             ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetWindowScrollMaxX(window), window->Scroll.y, GetWindowScrollMaxY(window));
9393             ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9394             ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
9395             ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9396             ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9397             if (!window->NavRectRel[0].IsInverted())
9398                 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);
9399             else
9400                 ImGui::BulletText("NavRectRel[0]: <None>");
9401             if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9402             if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9403             if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9404             if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9405             {
9406                 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9407                 {
9408                     const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
9409                     if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9410                     {
9411                         ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
9412                         for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9413                             ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
9414                         ImGui::TreePop();
9415                     }
9416                 }
9417                 ImGui::TreePop();
9418             }
9419             ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9420             ImGui::TreePop();
9421         }
9422 
9423         static void NodeTabBar(ImGuiTabBar* tab_bar)
9424         {
9425             // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
9426             char buf[256];
9427             char* p = buf;
9428             const char* buf_end = buf + IM_ARRAYSIZE(buf);
9429             ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9430             if (ImGui::TreeNode(tab_bar, "%s", buf))
9431             {
9432                 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9433                 {
9434                     const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9435                     ImGui::PushID(tab);
9436                     if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9437                     if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9438                     ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID);
9439                     ImGui::PopID();
9440                 }
9441                 ImGui::TreePop();
9442             }
9443         }
9444     };
9445 
9446     // Access private state, we are going to display the draw lists from last frame
9447     ImGuiContext& g = *GImGui;
9448     Funcs::NodeWindows(g.Windows, "Windows");
9449     if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9450     {
9451         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9452             Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9453         ImGui::TreePop();
9454     }
9455     if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9456     {
9457         for (int i = 0; i < g.OpenPopupStack.Size; i++)
9458         {
9459             ImGuiWindow* window = g.OpenPopupStack[i].Window;
9460             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" : "");
9461         }
9462         ImGui::TreePop();
9463     }
9464     if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
9465     {
9466         for (int n = 0; n < g.TabBars.Data.Size; n++)
9467             Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9468         ImGui::TreePop();
9469     }
9470     if (ImGui::TreeNode("Internal state"))
9471     {
9472         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9473         ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9474         ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9475         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
9476         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]);
9477         ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9478         ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9479         ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9480         ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9481         ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
9482         ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9483         ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9484         ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9485         ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9486         ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9487         ImGui::TreePop();
9488     }
9489 
9490 
9491     if (g.IO.KeyCtrl && show_window_begin_order)
9492     {
9493         for (int n = 0; n < g.Windows.Size; n++)
9494         {
9495             ImGuiWindow* window = g.Windows[n];
9496             if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
9497                 continue;
9498             char buf[32];
9499             ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
9500             float font_size = ImGui::GetFontSize() * 2;
9501             ImDrawList* overlay_draw_list = GetOverlayDrawList(window);
9502             overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9503             overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf);
9504         }
9505     }
9506     ImGui::End();
9507 }
9508 
9509 //-----------------------------------------------------------------------------
9510 
9511 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9512 // 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.
9513 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9514 #include "imgui_user.inl"
9515 #endif
9516 
9517 //-----------------------------------------------------------------------------
9518