1 // dear imgui, v1.80
2 // (main code and documentation)
3 
4 // Help:
5 // - Read FAQ at http://dearimgui.org/faq
6 // - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
7 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
8 // Read imgui.cpp for details, links and comments.
9 
10 // Resources:
11 // - FAQ                   http://dearimgui.org/faq
12 // - Homepage & latest     https://github.com/ocornut/imgui
13 // - Releases & changelog  https://github.com/ocornut/imgui/releases
14 // - Gallery               https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!)
15 // - Glossary              https://github.com/ocornut/imgui/wiki/Glossary
16 // - Wiki                  https://github.com/ocornut/imgui/wiki
17 // - Issues & support      https://github.com/ocornut/imgui/issues
18 
19 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
20 // See LICENSE.txt for copyright and licensing details (standard MIT License).
21 // This library is free but needs your support to sustain development and maintenance.
22 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
23 // Individuals: you can support continued development via donations. See docs/README or web page.
24 
25 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
26 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
27 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
28 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
29 // to a better solution or official support for them.
30 
31 /*
32 
33 Index of this file:
34 
35 DOCUMENTATION
36 
37 - MISSION STATEMENT
38 - END-USER GUIDE
39 - PROGRAMMER GUIDE
40   - READ FIRST
41   - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
42   - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
43   - HOW A SIMPLE APPLICATION MAY LOOK LIKE
44   - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
45   - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
46 - API BREAKING CHANGES (read me when you update!)
47 - FREQUENTLY ASKED QUESTIONS (FAQ)
48   - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
49 
50 CODE
51 (search for "[SECTION]" in the code to find them)
52 
53 // [SECTION] INCLUDES
54 // [SECTION] FORWARD DECLARATIONS
55 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
56 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
57 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
58 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
59 // [SECTION] MISC HELPERS/UTILITIES (File functions)
60 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
61 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
62 // [SECTION] ImGuiStorage
63 // [SECTION] ImGuiTextFilter
64 // [SECTION] ImGuiTextBuffer
65 // [SECTION] ImGuiListClipper
66 // [SECTION] STYLING
67 // [SECTION] RENDER HELPERS
68 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
69 // [SECTION] ERROR CHECKING
70 // [SECTION] LAYOUT
71 // [SECTION] SCROLLING
72 // [SECTION] TOOLTIPS
73 // [SECTION] POPUPS
74 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
75 // [SECTION] DRAG AND DROP
76 // [SECTION] LOGGING/CAPTURING
77 // [SECTION] SETTINGS
78 // [SECTION] PLATFORM DEPENDENT HELPERS
79 // [SECTION] METRICS/DEBUGGER WINDOW
80 
81 */
82 
83 //-----------------------------------------------------------------------------
84 // DOCUMENTATION
85 //-----------------------------------------------------------------------------
86 
87 /*
88 
89  MISSION STATEMENT
90  =================
91 
92  - Easy to use to create code-driven and data-driven tools.
93  - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
94  - Easy to hack and improve.
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.
99 
100  Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
101 
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://dearimgui.org/controls_sheets
128 
129 
130  PROGRAMMER GUIDE
131  ================
132 
133  READ FIRST
134  ----------
135  - Remember to read the FAQ (https://www.dearimgui.org/faq)
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 in the FAQ.
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 
156  HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
157  ----------------------------------------------
158  - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
159  - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over master.
160  - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
161  - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
162    If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
163    from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
164    likely be a comment about it. Please report any issue to the GitHub page!
165  - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
166  - Try to keep your copy of Dear ImGui reasonably up to date.
167 
168 
169  GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
170  ---------------------------------------------------------------
171  - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
172  - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
173  - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
174    It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
175  - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.
176  - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
177  - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
178    Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
179    phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
180  - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
181  - 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.
182 
183 
184  HOW A SIMPLE APPLICATION MAY LOOK LIKE
185  --------------------------------------
186  EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
187  The sub-folders in examples/ contains examples applications following this structure.
188 
189      // Application init: create a dear imgui context, setup some options, load fonts
190      ImGui::CreateContext();
191      ImGuiIO& io = ImGui::GetIO();
192      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
193      // TODO: Fill optional fields of the io structure later.
194      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
195 
196      // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
197      ImGui_ImplWin32_Init(hwnd);
198      ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
199 
200      // Application main loop
201      while (true)
202      {
203          // Feed inputs to dear imgui, start new frame
204          ImGui_ImplDX11_NewFrame();
205          ImGui_ImplWin32_NewFrame();
206          ImGui::NewFrame();
207 
208          // Any application code here
209          ImGui::Text("Hello, world!");
210 
211          // Render dear imgui into screen
212          ImGui::Render();
213          ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
214          g_pSwapChain->Present(1, 0);
215      }
216 
217      // Shutdown
218      ImGui_ImplDX11_Shutdown();
219      ImGui_ImplWin32_Shutdown();
220      ImGui::DestroyContext();
221 
222  EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
223 
224      // Application init: create a dear imgui context, setup some options, load fonts
225      ImGui::CreateContext();
226      ImGuiIO& io = ImGui::GetIO();
227      // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
228      // TODO: Fill optional fields of the io structure later.
229      // TODO: Load TTF/OTF fonts if you don't want to use the default font.
230 
231      // Build and load the texture atlas into a texture
232      // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
233      int width, height;
234      unsigned char* pixels = NULL;
235      io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
236 
237      // At this point you've got the texture data and you need to upload that your your graphic system:
238      // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
239      // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
240      MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
241      io.Fonts->TexID = (void*)texture;
242 
243      // Application main loop
244      while (true)
245      {
246         // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
247         // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
248         io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
249         io.DisplaySize.x = 1920.0f;             // set the current display width
250         io.DisplaySize.y = 1280.0f;             // set the current display height here
251         io.MousePos = my_mouse_pos;             // set the mouse position
252         io.MouseDown[0] = my_mouse_buttons[0];  // set the mouse button states
253         io.MouseDown[1] = my_mouse_buttons[1];
254 
255         // Call NewFrame(), after this point you can use ImGui::* functions anytime
256         // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
257         ImGui::NewFrame();
258 
259         // Most of your application code here
260         ImGui::Text("Hello, world!");
261         MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
262         MyGameRender(); // may use any Dear ImGui functions as well!
263 
264         // Render dear imgui, swap buffers
265         // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)
266         ImGui::EndFrame();
267         ImGui::Render();
268         ImDrawData* draw_data = ImGui::GetDrawData();
269         MyImGuiRenderFunction(draw_data);
270         SwapBuffers();
271      }
272 
273      // Shutdown
274      ImGui::DestroyContext();
275 
276  To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application,
277  you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
278  Please read the FAQ and example applications for details about this!
279 
280 
281  HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
282  ---------------------------------------------
283  The backends in impl_impl_XXX.cpp files contains many working implementations of a rendering function.
284 
285     void void MyImGuiRenderFunction(ImDrawData* draw_data)
286     {
287        // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
288        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
289        // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
290        // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
291        for (int n = 0; n < draw_data->CmdListsCount; n++)
292        {
293           const ImDrawList* cmd_list = draw_data->CmdLists[n];
294           const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
295           const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
296           for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
297           {
298              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
299              if (pcmd->UserCallback)
300              {
301                  pcmd->UserCallback(cmd_list, pcmd);
302              }
303              else
304              {
305                  // The texture for the draw call is specified by pcmd->TextureId.
306                  // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
307                  MyEngineBindTexture((MyTexture*)pcmd->TextureId);
308 
309                  // We are using scissoring to clip some objects. All low-level graphics API should supports it.
310                  // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
311                  //   (some elements visible outside their bounds) but you can fix that once everything else works!
312                  // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
313                  //   In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
314                  //   However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
315                  //   always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
316                  // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
317                  ImVec2 pos = draw_data->DisplayPos;
318                  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));
319 
320                  // Render 'pcmd->ElemCount/3' indexed triangles.
321                  // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.
322                  MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
323              }
324              idx_buffer += pcmd->ElemCount;
325           }
326        }
327     }
328 
329 
330  USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
331  ------------------------------------------
332  - The gamepad/keyboard navigation is fairly functional and keeps being improved.
333  - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
334  - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
335  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
336  - Keyboard:
337     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
338       NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
339     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
340       will be set. For more advanced uses, you may want to read from:
341        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
342        - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
343        - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
344       Please reach out if you think the game vs navigation input sharing could be improved.
345  - Gamepad:
346     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
347     - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
348       Note that io.NavInputs[] is cleared by EndFrame().
349     - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
350          0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
351     - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
352       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.).
353     - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
354     - 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
355       to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
356  - Mouse:
357     - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
358     - 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.
359     - 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.
360       Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
361       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.
362       When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
363       (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!)
364       (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
365        to set a boolean to ignore your other external mouse positions until the external source is moved again.)
366 
367 
368  API BREAKING CHANGES
369  ====================
370 
371  Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
372  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.
373  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.
374  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
375 
376  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
377                         - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
378                         - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg
379                         - ImGuiInputTextCallback              -> use ImGuiTextEditCallback
380                         - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData
381  - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
382  - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
383  - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
384  - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
385  - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
386  - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
387                         - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend
388                         - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
389                         - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
390                         - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT
391                         - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT
392                       - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
393                         - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
394                         - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
395  - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).
396  - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
397  - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
398  - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.
399  - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
400  - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!
401  - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
402                        replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
403                        worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
404                        - if you omitted the 'power' parameter (likely!), you are not affected.
405                        - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
406                        - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
407                        see https://github.com/ocornut/imgui/issues/3361 for all details.
408                        kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used.
409                        for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
410                      - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
411  - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
412  - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
413  - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
414  - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
415  - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
416  - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
417  - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
418  - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
419                        - ShowTestWindow()                    -> use ShowDemoWindow()
420                        - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
421                        - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
422                        - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
423                        - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
424                        - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
425                        - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
426                        - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
427                        - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
428  - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API.
429  - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).
430  - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.
431  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
432  - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
433  - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
434                        - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
435                        - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
436                        - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
437                        - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
438                        - ImFont::Glyph                       -> use ImFontGlyph
439  - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.
440                        if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.
441                        The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).
442                        If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
443  - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
444  - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
445  - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.
446  - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have
447                        overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.
448                        This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.
449                        Please reach out if you are affected.
450  - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
451  - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
452  - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.
453  - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
454  - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
455  - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
456  - 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 an arbitrary small value!
457  - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
458  - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
459  - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
460  - 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.
461  - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
462  - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
463  - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
464  - 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.
465                        If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
466  - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
467  - 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.
468                        NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
469                        Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
470  - 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).
471  - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
472  - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
473  - 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.
474  - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
475  - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
476  - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
477  - 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.).
478                        old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
479                        when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
480                        in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
481  - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
482  - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
483  - 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.
484                        If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
485                        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.
486                        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.
487  - 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",
488                        consistent with other functions. Kept redirection functions (will obsolete).
489  - 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.
490  - 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 backend ahead of merging the Nav branch).
491  - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
492  - 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.
493  - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
494  - 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.
495  - 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.
496  - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
497                        - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
498                        - removed Shutdown() function, as DestroyContext() serve this purpose.
499                        - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
500                        - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
501                        - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
502  - 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.
503  - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
504  - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
505  - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
506  - 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.
507  - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
508  - 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
509  - 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.
510  - 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.
511  - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
512  - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
513                      - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
514  - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
515  - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
516  - 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.
517  - 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.
518                        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.
519  - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
520  - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
521  - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
522  - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
523  - 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.
524  - 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.
525  - 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.
526                        removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
527                          IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
528                          IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
529                          IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
530  - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
531  - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
532  - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
533  - 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).
534  - 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 backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
535  - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
536                      - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
537                      - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
538  - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
539  - 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.
540  - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
541  - 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.
542  - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).
543  - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).
544  - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
545  - 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.
546                      - 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.
547                      - 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))'
548  - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
549  - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
550  - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
551  - 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().
552  - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
553  - 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.
554  - 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.
555  - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
556                        If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
557                        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:
558                        ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; 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); }
559                        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.
560  - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
561  - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
562  - 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).
563  - 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.
564  - 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).
565  - 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)
566  - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
567  - 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.
568  - 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.
569  - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
570  - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
571  - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
572                        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.
573                        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!
574  - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
575  - 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.
576  - 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
577  - 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.
578                        you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
579  - 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.
580                        this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
581                      - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
582                      - the signature of the io.RenderDrawListsFn handler has changed!
583                        old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
584                        new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
585                          parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
586                          ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
587                          ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
588                      - 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.
589                      - 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!
590                      - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
591  - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
592  - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
593  - 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.
594  - 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
595  - 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!
596  - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
597  - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
598  - 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.
599  - 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.
600  - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
601  - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
602  - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
603  - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
604  - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
605  - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
606  - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
607  - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
608  - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
609  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
610  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
611  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
612  - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
613  - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
614  - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
615  - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
616  - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
617  - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
618                        - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
619                        - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier;
620                        you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.
621  - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
622  - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
623  - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
624  - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
625  - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
626  - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
627  - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
628  - 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)
629  - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
630  - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
631  - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
632  - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
633  - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
634  - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
635 
636 
637  FREQUENTLY ASKED QUESTIONS (FAQ)
638  ================================
639 
640  Read all answers online:
641    https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
642  Read all answers locally (with a text editor or ideally a Markdown viewer):
643    docs/FAQ.md
644  Some answers are copied down here to facilitate searching in code.
645 
646  Q&A: Basics
647  ===========
648 
649  Q: Where is the documentation?
650  A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
651     - Run the examples/ and explore them.
652     - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
653     - The demo covers most features of Dear ImGui, so you can read the code and see its output.
654     - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
655     - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
656       examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
657     - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
658     - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
659     - Your programming IDE is your friend, find the type or function declaration to find comments
660       associated to it.
661 
662  Q: What is this library called?
663  Q: Which version should I get?
664  >> This library is called "Dear ImGui", please don't call it "ImGui" :)
665  >> See https://www.dearimgui.org/faq for details.
666 
667  Q&A: Integration
668  ================
669 
670  Q: How to get started?
671  A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
672 
673  Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
674  A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
675  >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
676 
677  Q. How can I enable keyboard controls?
678  Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
679  Q: I integrated Dear ImGui in my engine and little squares are showing instead of text..
680  Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
681  Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries..
682  >> See https://www.dearimgui.org/faq
683 
684  Q&A: Usage
685  ----------
686 
687  Q: Why is my widget not reacting when I click on it?
688  Q: How can I have widgets with an empty label?
689  Q: How can I have multiple widgets with the same label?
690  Q: How can I display an image? What is ImTextureID, how does it works?
691  Q: How can I use my own math types instead of ImVec2/ImVec4?
692  Q: How can I interact with standard C++ types (such as std::string and std::vector)?
693  Q: How can I display custom shapes? (using low-level ImDrawList API)
694  >> See https://www.dearimgui.org/faq
695 
696  Q&A: Fonts, Text
697  ================
698 
699  Q: How should I handle DPI in my application?
700  Q: How can I load a different font than the default?
701  Q: How can I easily use icons in my application?
702  Q: How can I load multiple fonts?
703  Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
704  >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
705 
706  Q&A: Concerns
707  =============
708 
709  Q: Who uses Dear ImGui?
710  Q: Can you create elaborate/serious tools with Dear ImGui?
711  Q: Can you reskin the look of Dear ImGui?
712  Q: Why using C++ (as opposed to C)?
713  >> See https://www.dearimgui.org/faq
714 
715  Q&A: Community
716  ==============
717 
718  Q: How can I help?
719  A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui!
720       We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
721       This is among the most useful thing you can do for Dear ImGui. With increased funding we can hire more people working on this project.
722     - Individuals: you can support continued development via PayPal donations. See README.
723     - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
724       and see how you want to help and can help!
725     - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
726       You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3488). Visuals are ideal as they inspire other programmers.
727       But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
728     - 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).
729 
730 */
731 
732 //-------------------------------------------------------------------------
733 // [SECTION] INCLUDES
734 //-------------------------------------------------------------------------
735 
736 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
737 #define _CRT_SECURE_NO_WARNINGS
738 #endif
739 
740 #include "imgui.h"
741 #ifndef IMGUI_DISABLE
742 
743 #ifndef IMGUI_DEFINE_MATH_OPERATORS
744 #define IMGUI_DEFINE_MATH_OPERATORS
745 #endif
746 #include "imgui_internal.h"
747 
748 // System includes
749 #include <ctype.h>      // toupper
750 #include <stdio.h>      // vsnprintf, sscanf, printf
751 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
752 #include <stddef.h>     // intptr_t
753 #else
754 #include <stdint.h>     // intptr_t
755 #endif
756 
757 // [Windows] OS specific includes (optional)
758 #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
759 #define IMGUI_DISABLE_WIN32_FUNCTIONS
760 #endif
761 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
762 #ifndef WIN32_LEAN_AND_MEAN
763 #define WIN32_LEAN_AND_MEAN
764 #endif
765 #ifndef NOMINMAX
766 #define NOMINMAX
767 #endif
768 #ifndef __MINGW32__
769 #include <Windows.h>        // _wfopen, OpenClipboard
770 #else
771 #include <windows.h>
772 #endif
773 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
774 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
775 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
776 #endif
777 #endif
778 
779 // [Apple] OS specific includes
780 #if defined(__APPLE__)
781 #include <TargetConditionals.h>
782 #endif
783 
784 // Visual Studio warnings
785 #ifdef _MSC_VER
786 #pragma warning (disable: 4127)             // condition expression is constant
787 #pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
788 #if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
789 #pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
790 #endif
791 #endif
792 
793 // Clang/GCC warnings with -Weverything
794 #if defined(__clang__)
795 #if __has_warning("-Wunknown-warning-option")
796 #pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
797 #endif
798 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
799 #pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
800 #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.
801 #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.
802 #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.
803 #pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
804 #pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
805 #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.
806 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
807 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
808 #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.
809 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
810 #elif defined(__GNUC__)
811 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
812 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
813 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
814 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
815 #pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
816 #pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
817 #pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
818 #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
819 #pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
820 #pragma GCC diagnostic ignored "-Wclass-memaccess"          // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
821 #endif
822 
823 // Debug options
824 #define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
825 #define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
826 #define IMGUI_DEBUG_INI_SETTINGS    0   // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
827 
828 // 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.
829 static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
830 static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
831 
832 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
833 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f;     // Extend outside and inside windows. Affect FindHoveredWindow().
834 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
835 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 2.00f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
836 
837 //-------------------------------------------------------------------------
838 // [SECTION] FORWARD DECLARATIONS
839 //-------------------------------------------------------------------------
840 
841 static void             SetCurrentWindow(ImGuiWindow* window);
842 static void             FindHoveredWindow();
843 static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
844 static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
845 
846 static void             AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
847 static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
848 
849 static ImRect           GetViewportRect();
850 
851 // Settings
852 static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
853 static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
854 static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
855 static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
856 static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
857 
858 // Platform Dependents default implementation for IO functions
859 static const char*      GetClipboardTextFn_DefaultImpl(void* user_data);
860 static void             SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
861 static void             ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
862 
863 namespace ImGui
864 {
865 // Navigation
866 static void             NavUpdate();
867 static void             NavUpdateWindowing();
868 static void             NavUpdateWindowingOverlay();
869 static void             NavUpdateMoveResult();
870 static void             NavUpdateInitResult();
871 static float            NavUpdatePageUpPageDown();
872 static inline void      NavUpdateAnyRequestFlag();
873 static void             NavEndFrame();
874 static bool             NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
875 static void             NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
876 static void             NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
877 static ImVec2           NavCalcPreferredRefPos();
878 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
879 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
880 static int              FindWindowFocusIndex(ImGuiWindow* window);
881 
882 // Error Checking
883 static void             ErrorCheckNewFrameSanityChecks();
884 static void             ErrorCheckEndFrameSanityChecks();
885 
886 // Misc
887 static void             UpdateSettings();
888 static void             UpdateMouseInputs();
889 static void             UpdateMouseWheel();
890 static void             UpdateTabFocus();
891 static void             UpdateDebugToolItemPicker();
892 static bool             UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
893 static void             RenderWindowOuterBorders(ImGuiWindow* window);
894 static void             RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
895 static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
896 
897 }
898 
899 //-----------------------------------------------------------------------------
900 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
901 //-----------------------------------------------------------------------------
902 
903 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
904 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
905 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
906 //    SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
907 //    In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
908 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
909 //    If you want thread-safety to allow N threads to access N different contexts, you can:
910 //    - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
911 //          struct ImGuiContext;
912 //          extern thread_local ImGuiContext* MyImGuiTLS;
913 //          #define GImGui MyImGuiTLS
914 //      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.
915 //    - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
916 //    - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
917 #ifndef GImGui
918 ImGuiContext*   GImGui = NULL;
919 #endif
920 
921 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
922 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
923 // 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.
924 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)925 static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)926 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
927 #else
MallocWrapper(size_t size,void * user_data)928 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)929 static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
930 #endif
931 
932 static void*  (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
933 static void   (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
934 static void*    GImAllocatorUserData = NULL;
935 
936 //-----------------------------------------------------------------------------
937 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
938 //-----------------------------------------------------------------------------
939 
ImGuiStyle()940 ImGuiStyle::ImGuiStyle()
941 {
942     Alpha                   = 1.0f;             // Global alpha applies to everything in ImGui
943     WindowPadding           = ImVec2(8,8);      // Padding within a window
944     WindowRounding          = 9.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
945     WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
946     WindowMinSize           = ImVec2(32,32);    // Minimum window size
947     WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text
948     WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
949     ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
950     ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
951     PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
952     PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
953     FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
954     FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
955     FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
956     ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
957     ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
958     CellPadding             = ImVec2(4,2);      // Padding within a table cell
959     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!
960     IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
961     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
962     ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
963     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar
964     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar
965     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
966     LogSliderDeadzone       = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
967     TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
968     TabBorderSize           = 0.0f;             // Thickness of border around tabs.
969     TabMinWidthForCloseButton = 0.0f;           // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
970     ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
971     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
972     SelectableTextAlign     = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
973     DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
974     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.
975     MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
976     AntiAliasedLines        = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
977     AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
978     AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
979     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.
980     CircleSegmentMaxError   = 1.60f;            // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
981 
982     // Default theme
983     ImGui::StyleColorsClassic(this);
984 }
985 
986 // 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.
987 // 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)988 void ImGuiStyle::ScaleAllSizes(float scale_factor)
989 {
990     WindowPadding = ImFloor(WindowPadding * scale_factor);
991     WindowRounding = ImFloor(WindowRounding * scale_factor);
992     WindowMinSize = ImFloor(WindowMinSize * scale_factor);
993     ChildRounding = ImFloor(ChildRounding * scale_factor);
994     PopupRounding = ImFloor(PopupRounding * scale_factor);
995     FramePadding = ImFloor(FramePadding * scale_factor);
996     FrameRounding = ImFloor(FrameRounding * scale_factor);
997     ItemSpacing = ImFloor(ItemSpacing * scale_factor);
998     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
999     CellPadding = ImFloor(CellPadding * scale_factor);
1000     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1001     IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1002     ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1003     ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1004     ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1005     GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1006     GrabRounding = ImFloor(GrabRounding * scale_factor);
1007     LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1008     TabRounding = ImFloor(TabRounding * scale_factor);
1009     TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1010     DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1011     DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1012     MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1013 }
1014 
ImGuiIO()1015 ImGuiIO::ImGuiIO()
1016 {
1017     // Most fields are initialized with zero
1018     memset(this, 0, sizeof(*this));
1019     IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here.
1020 
1021     // Settings
1022     ConfigFlags = ImGuiConfigFlags_None;
1023     BackendFlags = ImGuiBackendFlags_None;
1024     DisplaySize = ImVec2(-1.0f, -1.0f);
1025     DeltaTime = 1.0f / 60.0f;
1026     IniSavingRate = 5.0f;
1027     IniFilename = "imgui.ini";
1028     LogFilename = "imgui_log.txt";
1029     MouseDoubleClickTime = 0.30f;
1030     MouseDoubleClickMaxDist = 6.0f;
1031     for (int i = 0; i < ImGuiKey_COUNT; i++)
1032         KeyMap[i] = -1;
1033     KeyRepeatDelay = 0.275f;
1034     KeyRepeatRate = 0.050f;
1035     UserData = NULL;
1036 
1037     Fonts = NULL;
1038     FontGlobalScale = 1.0f;
1039     FontDefault = NULL;
1040     FontAllowUserScaling = false;
1041     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1042 
1043     // Miscellaneous options
1044     MouseDrawCursor = false;
1045 #ifdef __APPLE__
1046     ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1047 #else
1048     ConfigMacOSXBehaviors = false;
1049 #endif
1050     ConfigInputTextCursorBlink = true;
1051     ConfigWindowsResizeFromEdges = true;
1052     ConfigWindowsMoveFromTitleBarOnly = false;
1053     ConfigMemoryCompactTimer = 60.0f;
1054 
1055     // Platform Functions
1056     BackendPlatformName = BackendRendererName = NULL;
1057     BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1058     GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;   // Platform dependent default implementations
1059     SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1060     ClipboardUserData = NULL;
1061     ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1062     ImeWindowHandle = NULL;
1063 
1064     // Input (NB: we already have memset zero the entire structure!)
1065     MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1066     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1067     MouseDragThreshold = 6.0f;
1068     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1069     for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
1070     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1071 }
1072 
1073 // Pass in translated ASCII characters for text input.
1074 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1075 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1076 void ImGuiIO::AddInputCharacter(unsigned int c)
1077 {
1078     if (c != 0)
1079         InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1080 }
1081 
1082 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1083 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1084 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1085 {
1086     if (c == 0 && InputQueueSurrogate == 0)
1087         return;
1088 
1089     if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1090     {
1091         if (InputQueueSurrogate != 0)
1092             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1093         InputQueueSurrogate = c;
1094         return;
1095     }
1096 
1097     ImWchar cp = c;
1098     if (InputQueueSurrogate != 0)
1099     {
1100         if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1101             InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1102         else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang)
1103             cp = IM_UNICODE_CODEPOINT_INVALID;
1104         else
1105             cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1106         InputQueueSurrogate = 0;
1107     }
1108     InputQueueCharacters.push_back(cp);
1109 }
1110 
AddInputCharactersUTF8(const char * utf8_chars)1111 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1112 {
1113     while (*utf8_chars != 0)
1114     {
1115         unsigned int c = 0;
1116         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1117         if (c != 0)
1118             InputQueueCharacters.push_back((ImWchar)c);
1119     }
1120 }
1121 
ClearInputCharacters()1122 void ImGuiIO::ClearInputCharacters()
1123 {
1124     InputQueueCharacters.resize(0);
1125 }
1126 
1127 //-----------------------------------------------------------------------------
1128 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1129 //-----------------------------------------------------------------------------
1130 
ImBezierCubicClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1131 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1132 {
1133     IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau()
1134     ImVec2 p_last = p1;
1135     ImVec2 p_closest;
1136     float p_closest_dist2 = FLT_MAX;
1137     float t_step = 1.0f / (float)num_segments;
1138     for (int i_step = 1; i_step <= num_segments; i_step++)
1139     {
1140         ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1141         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1142         float dist2 = ImLengthSqr(p - p_line);
1143         if (dist2 < p_closest_dist2)
1144         {
1145             p_closest = p_line;
1146             p_closest_dist2 = dist2;
1147         }
1148         p_last = p_current;
1149     }
1150     return p_closest;
1151 }
1152 
1153 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
ImBezierCubicClosestPointCasteljauStep(const ImVec2 & p,ImVec2 & p_closest,ImVec2 & p_last,float & p_closest_dist2,float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,float tess_tol,int level)1154 static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1155 {
1156     float dx = x4 - x1;
1157     float dy = y4 - y1;
1158     float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1159     float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1160     d2 = (d2 >= 0) ? d2 : -d2;
1161     d3 = (d3 >= 0) ? d3 : -d3;
1162     if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1163     {
1164         ImVec2 p_current(x4, y4);
1165         ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1166         float dist2 = ImLengthSqr(p - p_line);
1167         if (dist2 < p_closest_dist2)
1168         {
1169             p_closest = p_line;
1170             p_closest_dist2 = dist2;
1171         }
1172         p_last = p_current;
1173     }
1174     else if (level < 10)
1175     {
1176         float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
1177         float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
1178         float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
1179         float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
1180         float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
1181         float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1182         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1183         ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1184     }
1185 }
1186 
1187 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1188 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
ImBezierCubicClosestPointCasteljau(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,float tess_tol)1189 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1190 {
1191     IM_ASSERT(tess_tol > 0.0f);
1192     ImVec2 p_last = p1;
1193     ImVec2 p_closest;
1194     float p_closest_dist2 = FLT_MAX;
1195     ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
1196     return p_closest;
1197 }
1198 
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1199 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1200 {
1201     ImVec2 ap = p - a;
1202     ImVec2 ab_dir = b - a;
1203     float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1204     if (dot < 0.0f)
1205         return a;
1206     float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1207     if (dot > ab_len_sqr)
1208         return b;
1209     return a + ab_dir * dot / ab_len_sqr;
1210 }
1211 
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1212 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1213 {
1214     bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1215     bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1216     bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1217     return ((b1 == b2) && (b2 == b3));
1218 }
1219 
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1220 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1221 {
1222     ImVec2 v0 = b - a;
1223     ImVec2 v1 = c - a;
1224     ImVec2 v2 = p - a;
1225     const float denom = v0.x * v1.y - v1.x * v0.y;
1226     out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1227     out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1228     out_u = 1.0f - out_v - out_w;
1229 }
1230 
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1231 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1232 {
1233     ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1234     ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1235     ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1236     float dist2_ab = ImLengthSqr(p - proj_ab);
1237     float dist2_bc = ImLengthSqr(p - proj_bc);
1238     float dist2_ca = ImLengthSqr(p - proj_ca);
1239     float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1240     if (m == dist2_ab)
1241         return proj_ab;
1242     if (m == dist2_bc)
1243         return proj_bc;
1244     return proj_ca;
1245 }
1246 
1247 //-----------------------------------------------------------------------------
1248 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1249 //-----------------------------------------------------------------------------
1250 
1251 // 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)1252 int ImStricmp(const char* str1, const char* str2)
1253 {
1254     int d;
1255     while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1256     return d;
1257 }
1258 
ImStrnicmp(const char * str1,const char * str2,size_t count)1259 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1260 {
1261     int d = 0;
1262     while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1263     return d;
1264 }
1265 
ImStrncpy(char * dst,const char * src,size_t count)1266 void ImStrncpy(char* dst, const char* src, size_t count)
1267 {
1268     if (count < 1)
1269         return;
1270     if (count > 1)
1271         strncpy(dst, src, count - 1);
1272     dst[count - 1] = 0;
1273 }
1274 
ImStrdup(const char * str)1275 char* ImStrdup(const char* str)
1276 {
1277     size_t len = strlen(str);
1278     void* buf = IM_ALLOC(len + 1);
1279     return (char*)memcpy(buf, (const void*)str, len + 1);
1280 }
1281 
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1282 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1283 {
1284     size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1285     size_t src_size = strlen(src) + 1;
1286     if (dst_buf_size < src_size)
1287     {
1288         IM_FREE(dst);
1289         dst = (char*)IM_ALLOC(src_size);
1290         if (p_dst_size)
1291             *p_dst_size = src_size;
1292     }
1293     return (char*)memcpy(dst, (const void*)src, src_size);
1294 }
1295 
ImStrchrRange(const char * str,const char * str_end,char c)1296 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1297 {
1298     const char* p = (const char*)memchr(str, (int)c, str_end - str);
1299     return p;
1300 }
1301 
ImStrlenW(const ImWchar * str)1302 int ImStrlenW(const ImWchar* str)
1303 {
1304     //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1305     int n = 0;
1306     while (*str++) n++;
1307     return n;
1308 }
1309 
1310 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1311 const char* ImStreolRange(const char* str, const char* str_end)
1312 {
1313     const char* p = (const char*)memchr(str, '\n', str_end - str);
1314     return p ? p : str_end;
1315 }
1316 
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1317 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1318 {
1319     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1320         buf_mid_line--;
1321     return buf_mid_line;
1322 }
1323 
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1324 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1325 {
1326     if (!needle_end)
1327         needle_end = needle + strlen(needle);
1328 
1329     const char un0 = (char)toupper(*needle);
1330     while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1331     {
1332         if (toupper(*haystack) == un0)
1333         {
1334             const char* b = needle + 1;
1335             for (const char* a = haystack + 1; b < needle_end; a++, b++)
1336                 if (toupper(*a) != toupper(*b))
1337                     break;
1338             if (b == needle_end)
1339                 return haystack;
1340         }
1341         haystack++;
1342     }
1343     return NULL;
1344 }
1345 
1346 // 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)1347 void ImStrTrimBlanks(char* buf)
1348 {
1349     char* p = buf;
1350     while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1351         p++;
1352     char* p_start = p;
1353     while (*p != 0)                         // Find end of string
1354         p++;
1355     while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1356         p--;
1357     if (p_start != buf)                     // Copy memory if we had leading blanks
1358         memmove(buf, p_start, p - p_start);
1359     buf[p - p_start] = 0;                   // Zero terminate
1360 }
1361 
ImStrSkipBlank(const char * str)1362 const char* ImStrSkipBlank(const char* str)
1363 {
1364     while (str[0] == ' ' || str[0] == '\t')
1365         str++;
1366     return str;
1367 }
1368 
1369 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1370 // 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.
1371 // B) When buf==NULL vsnprintf() will return the output size.
1372 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1373 
1374 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1375 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1376 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1377 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1378 #ifdef IMGUI_USE_STB_SPRINTF
1379 #define STB_SPRINTF_IMPLEMENTATION
1380 #include "stb_sprintf.h"
1381 #endif
1382 
1383 #if defined(_MSC_VER) && !defined(vsnprintf)
1384 #define vsnprintf _vsnprintf
1385 #endif
1386 
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1387 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1388 {
1389     va_list args;
1390     va_start(args, fmt);
1391 #ifdef IMGUI_USE_STB_SPRINTF
1392     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1393 #else
1394     int w = vsnprintf(buf, buf_size, fmt, args);
1395 #endif
1396     va_end(args);
1397     if (buf == NULL)
1398         return w;
1399     if (w == -1 || w >= (int)buf_size)
1400         w = (int)buf_size - 1;
1401     buf[w] = 0;
1402     return w;
1403 }
1404 
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1405 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1406 {
1407 #ifdef IMGUI_USE_STB_SPRINTF
1408     int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1409 #else
1410     int w = vsnprintf(buf, buf_size, fmt, args);
1411 #endif
1412     if (buf == NULL)
1413         return w;
1414     if (w == -1 || w >= (int)buf_size)
1415         w = (int)buf_size - 1;
1416     buf[w] = 0;
1417     return w;
1418 }
1419 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1420 
1421 // CRC32 needs a 1KB lookup table (not cache friendly)
1422 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1423 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1424 static const ImU32 GCrc32LookupTable[256] =
1425 {
1426     0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1427     0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1428     0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1429     0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1430     0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1431     0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1432     0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1433     0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1434     0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1435     0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1436     0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1437     0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1438     0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1439     0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1440     0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1441     0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1442 };
1443 
1444 // Known size hash
1445 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1446 // 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)1447 ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1448 {
1449     ImU32 crc = ~seed;
1450     const unsigned char* data = (const unsigned char*)data_p;
1451     const ImU32* crc32_lut = GCrc32LookupTable;
1452     while (data_size-- != 0)
1453         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1454     return ~crc;
1455 }
1456 
1457 // Zero-terminated string hash, with support for ### to reset back to seed value
1458 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1459 // Because this syntax is rarely used we are optimizing for the common case.
1460 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1461 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1462 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashStr(const char * data_p,size_t data_size,ImU32 seed)1463 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1464 {
1465     seed = ~seed;
1466     ImU32 crc = seed;
1467     const unsigned char* data = (const unsigned char*)data_p;
1468     const ImU32* crc32_lut = GCrc32LookupTable;
1469     if (data_size != 0)
1470     {
1471         while (data_size-- != 0)
1472         {
1473             unsigned char c = *data++;
1474             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1475                 crc = seed;
1476             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1477         }
1478     }
1479     else
1480     {
1481         while (unsigned char c = *data++)
1482         {
1483             if (c == '#' && data[0] == '#' && data[1] == '#')
1484                 crc = seed;
1485             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1486         }
1487     }
1488     return ~crc;
1489 }
1490 
1491 //-----------------------------------------------------------------------------
1492 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1493 //-----------------------------------------------------------------------------
1494 
1495 // Default file functions
1496 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1497 
ImFileOpen(const char * filename,const char * mode)1498 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1499 {
1500 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1501     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1502     // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1503     const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1504     const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1505     ImVector<ImWchar> buf;
1506     buf.resize(filename_wsize + mode_wsize);
1507     ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1508     ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1509     return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1510 #else
1511     return fopen(filename, mode);
1512 #endif
1513 }
1514 
1515 // We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
ImFileClose(ImFileHandle f)1516 bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1517 ImU64   ImFileGetSize(ImFileHandle f)   { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
ImFileRead(void * data,ImU64 sz,ImU64 count,ImFileHandle f)1518 ImU64   ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f)           { return fread(data, (size_t)sz, (size_t)count, f); }
ImFileWrite(const void * data,ImU64 sz,ImU64 count,ImFileHandle f)1519 ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1520 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1521 
1522 // Helper: Load file content into memory
1523 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1524 // This can't really be used with "rt" because fseek size won't match read size.
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1525 void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1526 {
1527     IM_ASSERT(filename && mode);
1528     if (out_file_size)
1529         *out_file_size = 0;
1530 
1531     ImFileHandle f;
1532     if ((f = ImFileOpen(filename, mode)) == NULL)
1533         return NULL;
1534 
1535     size_t file_size = (size_t)ImFileGetSize(f);
1536     if (file_size == (size_t)-1)
1537     {
1538         ImFileClose(f);
1539         return NULL;
1540     }
1541 
1542     void* file_data = IM_ALLOC(file_size + padding_bytes);
1543     if (file_data == NULL)
1544     {
1545         ImFileClose(f);
1546         return NULL;
1547     }
1548     if (ImFileRead(file_data, 1, file_size, f) != file_size)
1549     {
1550         ImFileClose(f);
1551         IM_FREE(file_data);
1552         return NULL;
1553     }
1554     if (padding_bytes > 0)
1555         memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1556 
1557     ImFileClose(f);
1558     if (out_file_size)
1559         *out_file_size = file_size;
1560 
1561     return file_data;
1562 }
1563 
1564 //-----------------------------------------------------------------------------
1565 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1566 //-----------------------------------------------------------------------------
1567 
1568 // Convert UTF-8 to 32-bit character, process single character input.
1569 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1570 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1571 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1572 {
1573     static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };
1574     static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1575     static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1576     static const int shiftc[] = { 0, 18, 12, 6, 0 };
1577     static const int shifte[] = { 0, 6, 4, 2, 0 };
1578     int len = lengths[*(const unsigned char*)in_text >> 3];
1579     int wanted = len + !len;
1580 
1581     if (in_text_end == NULL)
1582         in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1583 
1584     // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1585     // so it is fast even with excessive branching.
1586     unsigned char s[4];
1587     s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1588     s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1589     s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1590     s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1591 
1592     // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1593     *out_char  = (uint32_t)(s[0] & masks[len]) << 18;
1594     *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1595     *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;
1596     *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;
1597     *out_char >>= shiftc[len];
1598 
1599     // Accumulate the various error conditions.
1600     int e = 0;
1601     e  = (*out_char < mins[len]) << 6; // non-canonical encoding
1602     e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?
1603     e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range?
1604     e |= (s[1] & 0xc0) >> 2;
1605     e |= (s[2] & 0xc0) >> 4;
1606     e |= (s[3]       ) >> 6;
1607     e ^= 0x2a; // top two bits of each tail byte correct?
1608     e >>= shifte[len];
1609 
1610     if (e)
1611     {
1612         // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1613         // One byte is consumed in case of invalid first byte of in_text.
1614         // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1615         // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1616         wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1617         *out_char = IM_UNICODE_CODEPOINT_INVALID;
1618     }
1619 
1620     return wanted;
1621 }
1622 
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1623 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1624 {
1625     ImWchar* buf_out = buf;
1626     ImWchar* buf_end = buf + buf_size;
1627     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1628     {
1629         unsigned int c;
1630         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1631         if (c == 0)
1632             break;
1633         *buf_out++ = (ImWchar)c;
1634     }
1635     *buf_out = 0;
1636     if (in_text_remaining)
1637         *in_text_remaining = in_text;
1638     return (int)(buf_out - buf);
1639 }
1640 
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1641 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1642 {
1643     int char_count = 0;
1644     while ((!in_text_end || in_text < in_text_end) && *in_text)
1645     {
1646         unsigned int c;
1647         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1648         if (c == 0)
1649             break;
1650         char_count++;
1651     }
1652     return char_count;
1653 }
1654 
1655 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1656 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1657 {
1658     if (c < 0x80)
1659     {
1660         buf[0] = (char)c;
1661         return 1;
1662     }
1663     if (c < 0x800)
1664     {
1665         if (buf_size < 2) return 0;
1666         buf[0] = (char)(0xc0 + (c >> 6));
1667         buf[1] = (char)(0x80 + (c & 0x3f));
1668         return 2;
1669     }
1670     if (c < 0x10000)
1671     {
1672         if (buf_size < 3) return 0;
1673         buf[0] = (char)(0xe0 + (c >> 12));
1674         buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1675         buf[2] = (char)(0x80 + ((c ) & 0x3f));
1676         return 3;
1677     }
1678     if (c <= 0x10FFFF)
1679     {
1680         if (buf_size < 4) return 0;
1681         buf[0] = (char)(0xf0 + (c >> 18));
1682         buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1683         buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1684         buf[3] = (char)(0x80 + ((c ) & 0x3f));
1685         return 4;
1686     }
1687     // Invalid code point, the max unicode is 0x10FFFF
1688     return 0;
1689 }
1690 
1691 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1692 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1693 {
1694     unsigned int unused = 0;
1695     return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1696 }
1697 
ImTextCountUtf8BytesFromChar(unsigned int c)1698 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1699 {
1700     if (c < 0x80) return 1;
1701     if (c < 0x800) return 2;
1702     if (c < 0x10000) return 3;
1703     if (c <= 0x10FFFF) return 4;
1704     return 3;
1705 }
1706 
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1707 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1708 {
1709     char* buf_out = buf;
1710     const char* buf_end = buf + buf_size;
1711     while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1712     {
1713         unsigned int c = (unsigned int)(*in_text++);
1714         if (c < 0x80)
1715             *buf_out++ = (char)c;
1716         else
1717             buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);
1718     }
1719     *buf_out = 0;
1720     return (int)(buf_out - buf);
1721 }
1722 
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1723 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1724 {
1725     int bytes_count = 0;
1726     while ((!in_text_end || in_text < in_text_end) && *in_text)
1727     {
1728         unsigned int c = (unsigned int)(*in_text++);
1729         if (c < 0x80)
1730             bytes_count++;
1731         else
1732             bytes_count += ImTextCountUtf8BytesFromChar(c);
1733     }
1734     return bytes_count;
1735 }
1736 
1737 //-----------------------------------------------------------------------------
1738 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1739 // Note: The Convert functions are early design which are not consistent with other API.
1740 //-----------------------------------------------------------------------------
1741 
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1742 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1743 {
1744     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1745     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1746     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1747     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1748     return IM_COL32(r, g, b, 0xFF);
1749 }
1750 
ColorConvertU32ToFloat4(ImU32 in)1751 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1752 {
1753     float s = 1.0f / 255.0f;
1754     return ImVec4(
1755         ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1756         ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1757         ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1758         ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1759 }
1760 
ColorConvertFloat4ToU32(const ImVec4 & in)1761 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1762 {
1763     ImU32 out;
1764     out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1765     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1766     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1767     out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1768     return out;
1769 }
1770 
1771 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1772 // 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)1773 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1774 {
1775     float K = 0.f;
1776     if (g < b)
1777     {
1778         ImSwap(g, b);
1779         K = -1.f;
1780     }
1781     if (r < g)
1782     {
1783         ImSwap(r, g);
1784         K = -2.f / 6.f - K;
1785     }
1786 
1787     const float chroma = r - (g < b ? g : b);
1788     out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1789     out_s = chroma / (r + 1e-20f);
1790     out_v = r;
1791 }
1792 
1793 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1794 // 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)1795 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1796 {
1797     if (s == 0.0f)
1798     {
1799         // gray
1800         out_r = out_g = out_b = v;
1801         return;
1802     }
1803 
1804     h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1805     int   i = (int)h;
1806     float f = h - (float)i;
1807     float p = v * (1.0f - s);
1808     float q = v * (1.0f - s * f);
1809     float t = v * (1.0f - s * (1.0f - f));
1810 
1811     switch (i)
1812     {
1813     case 0: out_r = v; out_g = t; out_b = p; break;
1814     case 1: out_r = q; out_g = v; out_b = p; break;
1815     case 2: out_r = p; out_g = v; out_b = t; break;
1816     case 3: out_r = p; out_g = q; out_b = v; break;
1817     case 4: out_r = t; out_g = p; out_b = v; break;
1818     case 5: default: out_r = v; out_g = p; out_b = q; break;
1819     }
1820 }
1821 
1822 //-----------------------------------------------------------------------------
1823 // [SECTION] ImGuiStorage
1824 // Helper: Key->value storage
1825 //-----------------------------------------------------------------------------
1826 
1827 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1828 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1829 {
1830     ImGuiStorage::ImGuiStoragePair* first = data.Data;
1831     ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1832     size_t count = (size_t)(last - first);
1833     while (count > 0)
1834     {
1835         size_t count2 = count >> 1;
1836         ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1837         if (mid->key < key)
1838         {
1839             first = ++mid;
1840             count -= count2 + 1;
1841         }
1842         else
1843         {
1844             count = count2;
1845         }
1846     }
1847     return first;
1848 }
1849 
1850 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1851 void ImGuiStorage::BuildSortByKey()
1852 {
1853     struct StaticFunc
1854     {
1855         static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1856         {
1857             // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1858             if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1859             if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1860             return 0;
1861         }
1862     };
1863     if (Data.Size > 1)
1864         ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1865 }
1866 
GetInt(ImGuiID key,int default_val) const1867 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1868 {
1869     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1870     if (it == Data.end() || it->key != key)
1871         return default_val;
1872     return it->val_i;
1873 }
1874 
GetBool(ImGuiID key,bool default_val) const1875 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1876 {
1877     return GetInt(key, default_val ? 1 : 0) != 0;
1878 }
1879 
GetFloat(ImGuiID key,float default_val) const1880 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1881 {
1882     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1883     if (it == Data.end() || it->key != key)
1884         return default_val;
1885     return it->val_f;
1886 }
1887 
GetVoidPtr(ImGuiID key) const1888 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1889 {
1890     ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1891     if (it == Data.end() || it->key != key)
1892         return NULL;
1893     return it->val_p;
1894 }
1895 
1896 // 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)1897 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1898 {
1899     ImGuiStoragePair* it = LowerBound(Data, key);
1900     if (it == Data.end() || it->key != key)
1901         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1902     return &it->val_i;
1903 }
1904 
GetBoolRef(ImGuiID key,bool default_val)1905 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1906 {
1907     return (bool*)GetIntRef(key, default_val ? 1 : 0);
1908 }
1909 
GetFloatRef(ImGuiID key,float default_val)1910 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1911 {
1912     ImGuiStoragePair* it = LowerBound(Data, key);
1913     if (it == Data.end() || it->key != key)
1914         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1915     return &it->val_f;
1916 }
1917 
GetVoidPtrRef(ImGuiID key,void * default_val)1918 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1919 {
1920     ImGuiStoragePair* it = LowerBound(Data, key);
1921     if (it == Data.end() || it->key != key)
1922         it = Data.insert(it, ImGuiStoragePair(key, default_val));
1923     return &it->val_p;
1924 }
1925 
1926 // 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)1927 void ImGuiStorage::SetInt(ImGuiID key, int val)
1928 {
1929     ImGuiStoragePair* it = LowerBound(Data, key);
1930     if (it == Data.end() || it->key != key)
1931     {
1932         Data.insert(it, ImGuiStoragePair(key, val));
1933         return;
1934     }
1935     it->val_i = val;
1936 }
1937 
SetBool(ImGuiID key,bool val)1938 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1939 {
1940     SetInt(key, val ? 1 : 0);
1941 }
1942 
SetFloat(ImGuiID key,float val)1943 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1944 {
1945     ImGuiStoragePair* it = LowerBound(Data, key);
1946     if (it == Data.end() || it->key != key)
1947     {
1948         Data.insert(it, ImGuiStoragePair(key, val));
1949         return;
1950     }
1951     it->val_f = val;
1952 }
1953 
SetVoidPtr(ImGuiID key,void * val)1954 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1955 {
1956     ImGuiStoragePair* it = LowerBound(Data, key);
1957     if (it == Data.end() || it->key != key)
1958     {
1959         Data.insert(it, ImGuiStoragePair(key, val));
1960         return;
1961     }
1962     it->val_p = val;
1963 }
1964 
SetAllInt(int v)1965 void ImGuiStorage::SetAllInt(int v)
1966 {
1967     for (int i = 0; i < Data.Size; i++)
1968         Data[i].val_i = v;
1969 }
1970 
1971 //-----------------------------------------------------------------------------
1972 // [SECTION] ImGuiTextFilter
1973 //-----------------------------------------------------------------------------
1974 
1975 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1976 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1977 {
1978     if (default_filter)
1979     {
1980         ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1981         Build();
1982     }
1983     else
1984     {
1985         InputBuf[0] = 0;
1986         CountGrep = 0;
1987     }
1988 }
1989 
Draw(const char * label,float width)1990 bool ImGuiTextFilter::Draw(const char* label, float width)
1991 {
1992     if (width != 0.0f)
1993         ImGui::SetNextItemWidth(width);
1994     bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1995     if (value_changed)
1996         Build();
1997     return value_changed;
1998 }
1999 
split(char separator,ImVector<ImGuiTextRange> * out) const2000 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2001 {
2002     out->resize(0);
2003     const char* wb = b;
2004     const char* we = wb;
2005     while (we < e)
2006     {
2007         if (*we == separator)
2008         {
2009             out->push_back(ImGuiTextRange(wb, we));
2010             wb = we + 1;
2011         }
2012         we++;
2013     }
2014     if (wb != we)
2015         out->push_back(ImGuiTextRange(wb, we));
2016 }
2017 
Build()2018 void ImGuiTextFilter::Build()
2019 {
2020     Filters.resize(0);
2021     ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2022     input_range.split(',', &Filters);
2023 
2024     CountGrep = 0;
2025     for (int i = 0; i != Filters.Size; i++)
2026     {
2027         ImGuiTextRange& f = Filters[i];
2028         while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2029             f.b++;
2030         while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2031             f.e--;
2032         if (f.empty())
2033             continue;
2034         if (Filters[i].b[0] != '-')
2035             CountGrep += 1;
2036     }
2037 }
2038 
PassFilter(const char * text,const char * text_end) const2039 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2040 {
2041     if (Filters.empty())
2042         return true;
2043 
2044     if (text == NULL)
2045         text = "";
2046 
2047     for (int i = 0; i != Filters.Size; i++)
2048     {
2049         const ImGuiTextRange& f = Filters[i];
2050         if (f.empty())
2051             continue;
2052         if (f.b[0] == '-')
2053         {
2054             // Subtract
2055             if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2056                 return false;
2057         }
2058         else
2059         {
2060             // Grep
2061             if (ImStristr(text, text_end, f.b, f.e) != NULL)
2062                 return true;
2063         }
2064     }
2065 
2066     // Implicit * grep
2067     if (CountGrep == 0)
2068         return true;
2069 
2070     return false;
2071 }
2072 
2073 //-----------------------------------------------------------------------------
2074 // [SECTION] ImGuiTextBuffer
2075 //-----------------------------------------------------------------------------
2076 
2077 // On some platform vsnprintf() takes va_list by reference and modifies it.
2078 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2079 #ifndef va_copy
2080 #if defined(__GNUC__) || defined(__clang__)
2081 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2082 #else
2083 #define va_copy(dest, src) (dest = src)
2084 #endif
2085 #endif
2086 
2087 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2088 
append(const char * str,const char * str_end)2089 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2090 {
2091     int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2092 
2093     // Add zero-terminator the first time
2094     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2095     const int needed_sz = write_off + len;
2096     if (write_off + len >= Buf.Capacity)
2097     {
2098         int new_capacity = Buf.Capacity * 2;
2099         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2100     }
2101 
2102     Buf.resize(needed_sz);
2103     memcpy(&Buf[write_off - 1], str, (size_t)len);
2104     Buf[write_off - 1 + len] = 0;
2105 }
2106 
appendf(const char * fmt,...)2107 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2108 {
2109     va_list args;
2110     va_start(args, fmt);
2111     appendfv(fmt, args);
2112     va_end(args);
2113 }
2114 
2115 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2116 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2117 {
2118     va_list args_copy;
2119     va_copy(args_copy, args);
2120 
2121     int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2122     if (len <= 0)
2123     {
2124         va_end(args_copy);
2125         return;
2126     }
2127 
2128     // Add zero-terminator the first time
2129     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2130     const int needed_sz = write_off + len;
2131     if (write_off + len >= Buf.Capacity)
2132     {
2133         int new_capacity = Buf.Capacity * 2;
2134         Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2135     }
2136 
2137     Buf.resize(needed_sz);
2138     ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2139     va_end(args_copy);
2140 }
2141 
2142 //-----------------------------------------------------------------------------
2143 // [SECTION] ImGuiListClipper
2144 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2145 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2146 //-----------------------------------------------------------------------------
2147 
2148 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2149 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
GetSkipItemForListClipping()2150 static bool GetSkipItemForListClipping()
2151 {
2152     ImGuiContext& g = *GImGui;
2153     return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2154 }
2155 
2156 // Helper to calculate coarse clipping of large list of evenly sized items.
2157 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2158 // 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)2159 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2160 {
2161     ImGuiContext& g = *GImGui;
2162     ImGuiWindow* window = g.CurrentWindow;
2163     if (g.LogEnabled)
2164     {
2165         // If logging is active, do not perform any clipping
2166         *out_items_display_start = 0;
2167         *out_items_display_end = items_count;
2168         return;
2169     }
2170     if (GetSkipItemForListClipping())
2171     {
2172         *out_items_display_start = *out_items_display_end = 0;
2173         return;
2174     }
2175 
2176     // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2177     ImRect unclipped_rect = window->ClipRect;
2178     if (g.NavMoveRequest)
2179         unclipped_rect.Add(g.NavScoringRect);
2180     if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2181         unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
2182 
2183     const ImVec2 pos = window->DC.CursorPos;
2184     int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2185     int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2186 
2187     // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2188     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2189         start--;
2190     if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2191         end++;
2192 
2193     start = ImClamp(start, 0, items_count);
2194     end = ImClamp(end + 1, start, items_count);
2195     *out_items_display_start = start;
2196     *out_items_display_end = end;
2197 }
2198 
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2199 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2200 {
2201     // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2202     // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2203     // The clipper should probably have a 4th step to display the last item in a regular manner.
2204     ImGuiContext& g = *GImGui;
2205     ImGuiWindow* window = g.CurrentWindow;
2206     float off_y = pos_y - window->DC.CursorPos.y;
2207     window->DC.CursorPos.y = pos_y;
2208     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2209     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.
2210     window->DC.PrevLineSize.y = (line_height - g.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.
2211     if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2212         columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
2213     if (ImGuiTable* table = g.CurrentTable)
2214     {
2215         if (table->IsInsideRow)
2216             ImGui::TableEndRow(table);
2217         table->RowPosY2 = window->DC.CursorPos.y;
2218         const int row_increase = (int)((off_y / line_height) + 0.5f);
2219         //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2220         table->RowBgColorCounter += row_increase;
2221     }
2222 }
2223 
ImGuiListClipper()2224 ImGuiListClipper::ImGuiListClipper()
2225 {
2226     memset(this, 0, sizeof(*this));
2227     ItemsCount = -1;
2228 }
2229 
~ImGuiListClipper()2230 ImGuiListClipper::~ImGuiListClipper()
2231 {
2232     IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
2233 }
2234 
2235 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
2236 // Use case B: Begin() called from constructor with items_height>0
2237 // 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 items_count,float items_height)2238 void ImGuiListClipper::Begin(int items_count, float items_height)
2239 {
2240     ImGuiContext& g = *GImGui;
2241     ImGuiWindow* window = g.CurrentWindow;
2242 
2243     if (ImGuiTable* table = g.CurrentTable)
2244         if (table->IsInsideRow)
2245             ImGui::TableEndRow(table);
2246 
2247     StartPosY = window->DC.CursorPos.y;
2248     ItemsHeight = items_height;
2249     ItemsCount = items_count;
2250     ItemsFrozen = 0;
2251     StepNo = 0;
2252     DisplayStart = -1;
2253     DisplayEnd = 0;
2254 }
2255 
End()2256 void ImGuiListClipper::End()
2257 {
2258     if (ItemsCount < 0) // Already ended
2259         return;
2260 
2261     // 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.
2262     if (ItemsCount < INT_MAX && DisplayStart >= 0)
2263         SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
2264     ItemsCount = -1;
2265     StepNo = 3;
2266 }
2267 
Step()2268 bool ImGuiListClipper::Step()
2269 {
2270     ImGuiContext& g = *GImGui;
2271     ImGuiWindow* window = g.CurrentWindow;
2272 
2273     ImGuiTable* table = g.CurrentTable;
2274     if (table && table->IsInsideRow)
2275         ImGui::TableEndRow(table);
2276 
2277     // No items
2278     if (ItemsCount == 0 || GetSkipItemForListClipping())
2279     {
2280         End();
2281         return false;
2282     }
2283 
2284     // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
2285     if (StepNo == 0)
2286     {
2287         // While we are in frozen row state, keep displaying items one by one, unclipped
2288         // FIXME: Could be stored as a table-agnostic state.
2289         if (table != NULL && !table->IsUnfrozenRows)
2290         {
2291             DisplayStart = ItemsFrozen;
2292             DisplayEnd = ItemsFrozen + 1;
2293             ItemsFrozen++;
2294             return true;
2295         }
2296 
2297         StartPosY = window->DC.CursorPos.y;
2298         if (ItemsHeight <= 0.0f)
2299         {
2300             // Submit the first item so we can measure its height (generally it is 0..1)
2301             DisplayStart = ItemsFrozen;
2302             DisplayEnd = ItemsFrozen + 1;
2303             StepNo = 1;
2304             return true;
2305         }
2306 
2307         // Already has item height (given by user in Begin): skip to calculating step
2308         DisplayStart = DisplayEnd;
2309         StepNo = 2;
2310     }
2311 
2312     // Step 1: the clipper infer height from first element
2313     if (StepNo == 1)
2314     {
2315         IM_ASSERT(ItemsHeight <= 0.0f);
2316         if (table)
2317         {
2318             const float pos_y1 = table->RowPosY1;   // Using this instead of StartPosY to handle clipper straddling the frozen row
2319             const float pos_y2 = table->RowPosY2;   // Using this instead of CursorPos.y to take account of tallest cell.
2320             ItemsHeight = pos_y2 - pos_y1;
2321             window->DC.CursorPos.y = pos_y2;
2322         }
2323         else
2324         {
2325             ItemsHeight = window->DC.CursorPos.y - StartPosY;
2326         }
2327         IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
2328         StepNo = 2;
2329     }
2330 
2331     // Reached end of list
2332     if (DisplayEnd >= ItemsCount)
2333     {
2334         End();
2335         return false;
2336     }
2337 
2338     // Step 2: calculate the actual range of elements to display, and position the cursor before the first element
2339     if (StepNo == 2)
2340     {
2341         IM_ASSERT(ItemsHeight > 0.0f);
2342 
2343         int already_submitted = DisplayEnd;
2344         ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
2345         DisplayStart += already_submitted;
2346         DisplayEnd += already_submitted;
2347 
2348         // Seek cursor
2349         if (DisplayStart > already_submitted)
2350             SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
2351 
2352         StepNo = 3;
2353         return true;
2354     }
2355 
2356     // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
2357     // Advance the cursor to the end of the list and then returns 'false' to end the loop.
2358     if (StepNo == 3)
2359     {
2360         // Seek cursor
2361         if (ItemsCount < INT_MAX)
2362             SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
2363         ItemsCount = -1;
2364         return false;
2365     }
2366 
2367     IM_ASSERT(0);
2368     return false;
2369 }
2370 
2371 //-----------------------------------------------------------------------------
2372 // [SECTION] STYLING
2373 //-----------------------------------------------------------------------------
2374 
GetStyle()2375 ImGuiStyle& ImGui::GetStyle()
2376 {
2377     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2378     return GImGui->Style;
2379 }
2380 
GetColorU32(ImGuiCol idx,float alpha_mul)2381 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2382 {
2383     ImGuiStyle& style = GImGui->Style;
2384     ImVec4 c = style.Colors[idx];
2385     c.w *= style.Alpha * alpha_mul;
2386     return ColorConvertFloat4ToU32(c);
2387 }
2388 
GetColorU32(const ImVec4 & col)2389 ImU32 ImGui::GetColorU32(const ImVec4& col)
2390 {
2391     ImGuiStyle& style = GImGui->Style;
2392     ImVec4 c = col;
2393     c.w *= style.Alpha;
2394     return ColorConvertFloat4ToU32(c);
2395 }
2396 
GetStyleColorVec4(ImGuiCol idx)2397 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2398 {
2399     ImGuiStyle& style = GImGui->Style;
2400     return style.Colors[idx];
2401 }
2402 
GetColorU32(ImU32 col)2403 ImU32 ImGui::GetColorU32(ImU32 col)
2404 {
2405     ImGuiStyle& style = GImGui->Style;
2406     if (style.Alpha >= 1.0f)
2407         return col;
2408     ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2409     a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2410     return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2411 }
2412 
2413 // 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)2414 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2415 {
2416     ImGuiContext& g = *GImGui;
2417     ImGuiColorMod backup;
2418     backup.Col = idx;
2419     backup.BackupValue = g.Style.Colors[idx];
2420     g.ColorStack.push_back(backup);
2421     g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2422 }
2423 
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2424 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2425 {
2426     ImGuiContext& g = *GImGui;
2427     ImGuiColorMod backup;
2428     backup.Col = idx;
2429     backup.BackupValue = g.Style.Colors[idx];
2430     g.ColorStack.push_back(backup);
2431     g.Style.Colors[idx] = col;
2432 }
2433 
PopStyleColor(int count)2434 void ImGui::PopStyleColor(int count)
2435 {
2436     ImGuiContext& g = *GImGui;
2437     while (count > 0)
2438     {
2439         ImGuiColorMod& backup = g.ColorStack.back();
2440         g.Style.Colors[backup.Col] = backup.BackupValue;
2441         g.ColorStack.pop_back();
2442         count--;
2443     }
2444 }
2445 
2446 struct ImGuiStyleVarInfo
2447 {
2448     ImGuiDataType   Type;
2449     ImU32           Count;
2450     ImU32           Offset;
GetVarPtrImGuiStyleVarInfo2451     void*           GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2452 };
2453 
2454 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2455 {
2456     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) },               // ImGuiStyleVar_Alpha
2457     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) },       // ImGuiStyleVar_WindowPadding
2458     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) },      // ImGuiStyleVar_WindowRounding
2459     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) },    // ImGuiStyleVar_WindowBorderSize
2460     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) },       // ImGuiStyleVar_WindowMinSize
2461     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) },    // ImGuiStyleVar_WindowTitleAlign
2462     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) },       // ImGuiStyleVar_ChildRounding
2463     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) },     // ImGuiStyleVar_ChildBorderSize
2464     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) },       // ImGuiStyleVar_PopupRounding
2465     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) },     // ImGuiStyleVar_PopupBorderSize
2466     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) },        // ImGuiStyleVar_FramePadding
2467     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) },       // ImGuiStyleVar_FrameRounding
2468     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) },     // ImGuiStyleVar_FrameBorderSize
2469     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) },         // ImGuiStyleVar_ItemSpacing
2470     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) },    // ImGuiStyleVar_ItemInnerSpacing
2471     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) },       // ImGuiStyleVar_IndentSpacing
2472     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) },         // ImGuiStyleVar_CellPadding
2473     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) },       // ImGuiStyleVar_ScrollbarSize
2474     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },   // ImGuiStyleVar_ScrollbarRounding
2475     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },         // ImGuiStyleVar_GrabMinSize
2476     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },        // ImGuiStyleVar_GrabRounding
2477     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },         // ImGuiStyleVar_TabRounding
2478     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },     // ImGuiStyleVar_ButtonTextAlign
2479     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2480 };
2481 
GetStyleVarInfo(ImGuiStyleVar idx)2482 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2483 {
2484     IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2485     IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2486     return &GStyleVarInfo[idx];
2487 }
2488 
PushStyleVar(ImGuiStyleVar idx,float val)2489 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2490 {
2491     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2492     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2493     {
2494         ImGuiContext& g = *GImGui;
2495         float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2496         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2497         *pvar = val;
2498         return;
2499     }
2500     IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2501 }
2502 
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2503 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2504 {
2505     const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2506     if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2507     {
2508         ImGuiContext& g = *GImGui;
2509         ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2510         g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2511         *pvar = val;
2512         return;
2513     }
2514     IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2515 }
2516 
PopStyleVar(int count)2517 void ImGui::PopStyleVar(int count)
2518 {
2519     ImGuiContext& g = *GImGui;
2520     while (count > 0)
2521     {
2522         // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2523         ImGuiStyleMod& backup = g.StyleVarStack.back();
2524         const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2525         void* data = info->GetVarPtr(&g.Style);
2526         if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
2527         else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2528         g.StyleVarStack.pop_back();
2529         count--;
2530     }
2531 }
2532 
GetStyleColorName(ImGuiCol idx)2533 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2534 {
2535     // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2536     switch (idx)
2537     {
2538     case ImGuiCol_Text: return "Text";
2539     case ImGuiCol_TextDisabled: return "TextDisabled";
2540     case ImGuiCol_WindowBg: return "WindowBg";
2541     case ImGuiCol_ChildBg: return "ChildBg";
2542     case ImGuiCol_PopupBg: return "PopupBg";
2543     case ImGuiCol_Border: return "Border";
2544     case ImGuiCol_BorderShadow: return "BorderShadow";
2545     case ImGuiCol_FrameBg: return "FrameBg";
2546     case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2547     case ImGuiCol_FrameBgActive: return "FrameBgActive";
2548     case ImGuiCol_TitleBg: return "TitleBg";
2549     case ImGuiCol_TitleBgActive: return "TitleBgActive";
2550     case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2551     case ImGuiCol_MenuBarBg: return "MenuBarBg";
2552     case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2553     case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2554     case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2555     case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2556     case ImGuiCol_CheckMark: return "CheckMark";
2557     case ImGuiCol_SliderGrab: return "SliderGrab";
2558     case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2559     case ImGuiCol_Button: return "Button";
2560     case ImGuiCol_ButtonHovered: return "ButtonHovered";
2561     case ImGuiCol_ButtonActive: return "ButtonActive";
2562     case ImGuiCol_Header: return "Header";
2563     case ImGuiCol_HeaderHovered: return "HeaderHovered";
2564     case ImGuiCol_HeaderActive: return "HeaderActive";
2565     case ImGuiCol_Separator: return "Separator";
2566     case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2567     case ImGuiCol_SeparatorActive: return "SeparatorActive";
2568     case ImGuiCol_ResizeGrip: return "ResizeGrip";
2569     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2570     case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2571     case ImGuiCol_Tab: return "Tab";
2572     case ImGuiCol_TabHovered: return "TabHovered";
2573     case ImGuiCol_TabActive: return "TabActive";
2574     case ImGuiCol_TabUnfocused: return "TabUnfocused";
2575     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2576     case ImGuiCol_PlotLines: return "PlotLines";
2577     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2578     case ImGuiCol_PlotHistogram: return "PlotHistogram";
2579     case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2580     case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
2581     case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
2582     case ImGuiCol_TableBorderLight: return "TableBorderLight";
2583     case ImGuiCol_TableRowBg: return "TableRowBg";
2584     case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
2585     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2586     case ImGuiCol_DragDropTarget: return "DragDropTarget";
2587     case ImGuiCol_NavHighlight: return "NavHighlight";
2588     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2589     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2590     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2591     }
2592     IM_ASSERT(0);
2593     return "Unknown";
2594 }
2595 
2596 
2597 //-----------------------------------------------------------------------------
2598 // [SECTION] RENDER HELPERS
2599 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2600 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2601 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2602 //-----------------------------------------------------------------------------
2603 
FindRenderedTextEnd(const char * text,const char * text_end)2604 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2605 {
2606     const char* text_display_end = text;
2607     if (!text_end)
2608         text_end = (const char*)-1;
2609 
2610     while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2611         text_display_end++;
2612     return text_display_end;
2613 }
2614 
2615 // Internal ImGui functions to render text
2616 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2617 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2618 {
2619     ImGuiContext& g = *GImGui;
2620     ImGuiWindow* window = g.CurrentWindow;
2621 
2622     // Hide anything after a '##' string
2623     const char* text_display_end;
2624     if (hide_text_after_hash)
2625     {
2626         text_display_end = FindRenderedTextEnd(text, text_end);
2627     }
2628     else
2629     {
2630         if (!text_end)
2631             text_end = text + strlen(text); // FIXME-OPT
2632         text_display_end = text_end;
2633     }
2634 
2635     if (text != text_display_end)
2636     {
2637         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2638         if (g.LogEnabled)
2639             LogRenderedText(&pos, text, text_display_end);
2640     }
2641 }
2642 
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2643 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2644 {
2645     ImGuiContext& g = *GImGui;
2646     ImGuiWindow* window = g.CurrentWindow;
2647 
2648     if (!text_end)
2649         text_end = text + strlen(text); // FIXME-OPT
2650 
2651     if (text != text_end)
2652     {
2653         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2654         if (g.LogEnabled)
2655             LogRenderedText(&pos, text, text_end);
2656     }
2657 }
2658 
2659 // Default clip_rect uses (pos_min,pos_max)
2660 // 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)2661 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)
2662 {
2663     // Perform CPU side clipping for single clipped element to avoid using scissor state
2664     ImVec2 pos = pos_min;
2665     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2666 
2667     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2668     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2669     bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2670     if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2671         need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2672 
2673     // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2674     if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2675     if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2676 
2677     // Render
2678     if (need_clipping)
2679     {
2680         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2681         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2682     }
2683     else
2684     {
2685         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2686     }
2687 }
2688 
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)2689 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)
2690 {
2691     // Hide anything after a '##' string
2692     const char* text_display_end = FindRenderedTextEnd(text, text_end);
2693     const int text_len = (int)(text_display_end - text);
2694     if (text_len == 0)
2695         return;
2696 
2697     ImGuiContext& g = *GImGui;
2698     ImGuiWindow* window = g.CurrentWindow;
2699     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2700     if (g.LogEnabled)
2701         LogRenderedText(&pos_min, text, text_display_end);
2702 }
2703 
2704 
2705 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2706 // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display.
2707 // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
RenderTextEllipsis(ImDrawList * draw_list,const ImVec2 & pos_min,const ImVec2 & pos_max,float clip_max_x,float ellipsis_max_x,const char * text,const char * text_end_full,const ImVec2 * text_size_if_known)2708 void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
2709 {
2710     ImGuiContext& g = *GImGui;
2711     if (text_end_full == NULL)
2712         text_end_full = FindRenderedTextEnd(text);
2713     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2714 
2715     //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255));
2716     //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255));
2717     //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2718     // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2719     if (text_size.x > pos_max.x - pos_min.x)
2720     {
2721         // Hello wo...
2722         // |       |   |
2723         // min   max   ellipsis_max
2724         //          <-> this is generally some padding value
2725 
2726         const ImFont* font = draw_list->_Data->Font;
2727         const float font_size = draw_list->_Data->FontSize;
2728         const char* text_end_ellipsis = NULL;
2729 
2730         ImWchar ellipsis_char = font->EllipsisChar;
2731         int ellipsis_char_count = 1;
2732         if (ellipsis_char == (ImWchar)-1)
2733         {
2734             ellipsis_char = (ImWchar)'.';
2735             ellipsis_char_count = 3;
2736         }
2737         const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2738 
2739         float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
2740         float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
2741 
2742         if (ellipsis_char_count > 1)
2743         {
2744             // Full ellipsis size without free spacing after it.
2745             const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2746             ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2747             ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2748         }
2749 
2750         // We can now claim the space between pos_max.x and ellipsis_max.x
2751         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2752         float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2753         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2754         {
2755             // Always display at least 1 character if there's no room for character + ellipsis
2756             text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2757             text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2758         }
2759         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2760         {
2761             // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
2762             text_end_ellipsis--;
2763             text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
2764         }
2765 
2766         // Render text, render ellipsis
2767         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2768         float ellipsis_x = pos_min.x + text_size_clipped_x;
2769         if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2770             for (int i = 0; i < ellipsis_char_count; i++)
2771             {
2772                 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2773                 ellipsis_x += ellipsis_glyph_width;
2774             }
2775     }
2776     else
2777     {
2778         RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2779     }
2780 
2781     if (g.LogEnabled)
2782         LogRenderedText(&pos_min, text, text_end_full);
2783 }
2784 
2785 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2786 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2787 {
2788     ImGuiContext& g = *GImGui;
2789     ImGuiWindow* window = g.CurrentWindow;
2790     window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2791     const float border_size = g.Style.FrameBorderSize;
2792     if (border && border_size > 0.0f)
2793     {
2794         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2795         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2796     }
2797 }
2798 
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2799 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2800 {
2801     ImGuiContext& g = *GImGui;
2802     ImGuiWindow* window = g.CurrentWindow;
2803     const float border_size = g.Style.FrameBorderSize;
2804     if (border_size > 0.0f)
2805     {
2806         window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2807         window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2808     }
2809 }
2810 
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2811 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2812 {
2813     ImGuiContext& g = *GImGui;
2814     if (id != g.NavId)
2815         return;
2816     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2817         return;
2818     ImGuiWindow* window = g.CurrentWindow;
2819     if (window->DC.NavHideHighlightOneFrame)
2820         return;
2821 
2822     float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2823     ImRect display_rect = bb;
2824     display_rect.ClipWith(window->ClipRect);
2825     if (flags & ImGuiNavHighlightFlags_TypeDefault)
2826     {
2827         const float THICKNESS = 2.0f;
2828         const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2829         display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2830         bool fully_visible = window->ClipRect.Contains(display_rect);
2831         if (!fully_visible)
2832             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2833         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);
2834         if (!fully_visible)
2835             window->DrawList->PopClipRect();
2836     }
2837     if (flags & ImGuiNavHighlightFlags_TypeThin)
2838     {
2839         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2840     }
2841 }
2842 
2843 //-----------------------------------------------------------------------------
2844 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2845 //-----------------------------------------------------------------------------
2846 
2847 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2848 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
2849 {
2850     memset(this, 0, sizeof(*this));
2851     Name = ImStrdup(name);
2852     NameBufLen = (int)strlen(name) + 1;
2853     ID = ImHashStr(name);
2854     IDStack.push_back(ID);
2855     MoveId = GetID("#MOVE");
2856     ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2857     ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2858     AutoFitFramesX = AutoFitFramesY = -1;
2859     AutoPosLastDirection = ImGuiDir_None;
2860     SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2861     SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2862     LastFrameActive = -1;
2863     LastTimeActive = -1.0f;
2864     FontWindowScale = 1.0f;
2865     SettingsOffset = -1;
2866     DrawList = &DrawListInst;
2867     DrawList->_Data = &context->DrawListSharedData;
2868     DrawList->_OwnerName = Name;
2869 }
2870 
~ImGuiWindow()2871 ImGuiWindow::~ImGuiWindow()
2872 {
2873     IM_ASSERT(DrawList == &DrawListInst);
2874     IM_DELETE(Name);
2875     for (int i = 0; i != ColumnsStorage.Size; i++)
2876         ColumnsStorage[i].~ImGuiOldColumns();
2877 }
2878 
GetID(const char * str,const char * str_end)2879 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2880 {
2881     ImGuiID seed = IDStack.back();
2882     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2883     ImGui::KeepAliveID(id);
2884 #ifdef IMGUI_ENABLE_TEST_ENGINE
2885     ImGuiContext& g = *GImGui;
2886     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2887 #endif
2888     return id;
2889 }
2890 
GetID(const void * ptr)2891 ImGuiID ImGuiWindow::GetID(const void* ptr)
2892 {
2893     ImGuiID seed = IDStack.back();
2894     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2895     ImGui::KeepAliveID(id);
2896 #ifdef IMGUI_ENABLE_TEST_ENGINE
2897     ImGuiContext& g = *GImGui;
2898     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2899 #endif
2900     return id;
2901 }
2902 
GetID(int n)2903 ImGuiID ImGuiWindow::GetID(int n)
2904 {
2905     ImGuiID seed = IDStack.back();
2906     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2907     ImGui::KeepAliveID(id);
2908 #ifdef IMGUI_ENABLE_TEST_ENGINE
2909     ImGuiContext& g = *GImGui;
2910     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2911 #endif
2912     return id;
2913 }
2914 
GetIDNoKeepAlive(const char * str,const char * str_end)2915 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2916 {
2917     ImGuiID seed = IDStack.back();
2918     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2919 #ifdef IMGUI_ENABLE_TEST_ENGINE
2920     ImGuiContext& g = *GImGui;
2921     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2922 #endif
2923     return id;
2924 }
2925 
GetIDNoKeepAlive(const void * ptr)2926 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2927 {
2928     ImGuiID seed = IDStack.back();
2929     ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2930 #ifdef IMGUI_ENABLE_TEST_ENGINE
2931     ImGuiContext& g = *GImGui;
2932     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2933 #endif
2934     return id;
2935 }
2936 
GetIDNoKeepAlive(int n)2937 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2938 {
2939     ImGuiID seed = IDStack.back();
2940     ImGuiID id = ImHashData(&n, sizeof(n), seed);
2941 #ifdef IMGUI_ENABLE_TEST_ENGINE
2942     ImGuiContext& g = *GImGui;
2943     IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2944 #endif
2945     return id;
2946 }
2947 
2948 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2949 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2950 {
2951     ImGuiID seed = IDStack.back();
2952     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) };
2953     ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2954     ImGui::KeepAliveID(id);
2955     return id;
2956 }
2957 
SetCurrentWindow(ImGuiWindow * window)2958 static void SetCurrentWindow(ImGuiWindow* window)
2959 {
2960     ImGuiContext& g = *GImGui;
2961     g.CurrentWindow = window;
2962     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
2963     if (window)
2964         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2965 }
2966 
GcCompactTransientMiscBuffers()2967 void ImGui::GcCompactTransientMiscBuffers()
2968 {
2969     ImGuiContext& g = *GImGui;
2970     g.ItemFlagsStack.clear();
2971     g.GroupStack.clear();
2972     TableGcCompactSettings();
2973 }
2974 
2975 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2976 // Not freed:
2977 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
2978 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2979 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2980 {
2981     window->MemoryCompacted = true;
2982     window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2983     window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2984     window->IDStack.clear();
2985     window->DrawList->_ClearFreeMemory();
2986     window->DC.ChildWindows.clear();
2987     window->DC.ItemWidthStack.clear();
2988     window->DC.TextWrapPosStack.clear();
2989 }
2990 
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2991 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2992 {
2993     // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2994     // The other buffers tends to amortize much faster.
2995     window->MemoryCompacted = false;
2996     window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2997     window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2998     window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2999 }
3000 
SetActiveID(ImGuiID id,ImGuiWindow * window)3001 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
3002 {
3003     ImGuiContext& g = *GImGui;
3004     g.ActiveIdIsJustActivated = (g.ActiveId != id);
3005     if (g.ActiveIdIsJustActivated)
3006     {
3007         g.ActiveIdTimer = 0.0f;
3008         g.ActiveIdHasBeenPressedBefore = false;
3009         g.ActiveIdHasBeenEditedBefore = false;
3010         if (id != 0)
3011         {
3012             g.LastActiveId = id;
3013             g.LastActiveIdTimer = 0.0f;
3014         }
3015     }
3016     g.ActiveId = id;
3017     g.ActiveIdAllowOverlap = false;
3018     g.ActiveIdNoClearOnFocusLoss = false;
3019     g.ActiveIdWindow = window;
3020     g.ActiveIdHasBeenEditedThisFrame = false;
3021     if (id)
3022     {
3023         g.ActiveIdIsAlive = id;
3024         g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3025     }
3026 
3027     // Clear declaration of inputs claimed by the widget
3028     // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3029     g.ActiveIdUsingMouseWheel = false;
3030     g.ActiveIdUsingNavDirMask = 0x00;
3031     g.ActiveIdUsingNavInputMask = 0x00;
3032     g.ActiveIdUsingKeyInputMask = 0x00;
3033 }
3034 
ClearActiveID()3035 void ImGui::ClearActiveID()
3036 {
3037     SetActiveID(0, NULL); // g.ActiveId = 0;
3038 }
3039 
SetHoveredID(ImGuiID id)3040 void ImGui::SetHoveredID(ImGuiID id)
3041 {
3042     ImGuiContext& g = *GImGui;
3043     g.HoveredId = id;
3044     g.HoveredIdAllowOverlap = false;
3045     g.HoveredIdUsingMouseWheel = false;
3046     if (id != 0 && g.HoveredIdPreviousFrame != id)
3047         g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3048 }
3049 
GetHoveredID()3050 ImGuiID ImGui::GetHoveredID()
3051 {
3052     ImGuiContext& g = *GImGui;
3053     return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3054 }
3055 
KeepAliveID(ImGuiID id)3056 void ImGui::KeepAliveID(ImGuiID id)
3057 {
3058     ImGuiContext& g = *GImGui;
3059     if (g.ActiveId == id)
3060         g.ActiveIdIsAlive = id;
3061     if (g.ActiveIdPreviousFrame == id)
3062         g.ActiveIdPreviousFrameIsAlive = true;
3063 }
3064 
MarkItemEdited(ImGuiID id)3065 void ImGui::MarkItemEdited(ImGuiID id)
3066 {
3067     // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3068     // 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.
3069     ImGuiContext& g = *GImGui;
3070     IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3071     IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3072     //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3073     g.ActiveIdHasBeenEditedThisFrame = true;
3074     g.ActiveIdHasBeenEditedBefore = true;
3075     g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
3076 }
3077 
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3078 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3079 {
3080     // An active popup disable hovering on other windows (apart from its own children)
3081     // FIXME-OPT: This could be cached/stored within the window.
3082     ImGuiContext& g = *GImGui;
3083     if (g.NavWindow)
3084         if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3085             if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3086             {
3087                 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3088                 // NB: The order of those two tests is important because Modal windows are also Popups.
3089                 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3090                     return false;
3091                 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3092                     return false;
3093             }
3094     return true;
3095 }
3096 
3097 // This is roughly matching the behavior of internal-facing ItemHoverable()
3098 // - 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()
3099 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3100 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3101 {
3102     ImGuiContext& g = *GImGui;
3103     ImGuiWindow* window = g.CurrentWindow;
3104     if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3105         return IsItemFocused();
3106 
3107     // Test for bounding box overlap, as updated as ItemAdd()
3108     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
3109         return false;
3110     IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0);   // Flags not supported by this function
3111 
3112     // Test if we are hovering the right window (our window could be behind another window)
3113     // [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.
3114     // 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.
3115     //if (g.HoveredWindow != window)
3116     //    return false;
3117     if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
3118         return false;
3119 
3120     // Test if another item is active (e.g. being dragged)
3121     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
3122         if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3123             return false;
3124 
3125     // Test if interactions on this window are blocked by an active popup or modal.
3126     // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3127     if (!IsWindowContentHoverable(window, flags))
3128         return false;
3129 
3130     // Test if the item is disabled
3131     if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3132         return false;
3133 
3134     // Special handling for calling after Begin() which represent the title bar or tab.
3135     // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3136     if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
3137         return false;
3138     return true;
3139 }
3140 
3141 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3142 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3143 {
3144     ImGuiContext& g = *GImGui;
3145     if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3146         return false;
3147 
3148     ImGuiWindow* window = g.CurrentWindow;
3149     if (g.HoveredWindow != window)
3150         return false;
3151     if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3152         return false;
3153     if (!IsMouseHoveringRect(bb.Min, bb.Max))
3154         return false;
3155     if (g.NavDisableMouseHover)
3156         return false;
3157     if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled))
3158     {
3159         g.HoveredIdDisabled = true;
3160         return false;
3161     }
3162 
3163     // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3164     // hover test in widgets code. We could also decide to split this function is two.
3165     if (id != 0)
3166     {
3167         SetHoveredID(id);
3168 
3169         // [DEBUG] Item Picker tool!
3170         // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3171         // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3172         // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3173         // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3174         if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3175             GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3176         if (g.DebugItemPickerBreakId == id)
3177             IM_DEBUG_BREAK();
3178     }
3179 
3180     return true;
3181 }
3182 
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3183 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3184 {
3185     ImGuiContext& g = *GImGui;
3186     ImGuiWindow* window = g.CurrentWindow;
3187     if (!bb.Overlaps(window->ClipRect))
3188         if (id == 0 || (id != g.ActiveId && id != g.NavId))
3189             if (clip_even_when_logged || !g.LogEnabled)
3190                 return true;
3191     return false;
3192 }
3193 
3194 // This is also inlined in ItemAdd()
3195 // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect!
SetLastItemData(ImGuiWindow * window,ImGuiID item_id,ImGuiItemStatusFlags item_flags,const ImRect & item_rect)3196 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
3197 {
3198     window->DC.LastItemId = item_id;
3199     window->DC.LastItemStatusFlags = item_flags;
3200     window->DC.LastItemRect = item_rect;
3201 }
3202 
3203 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)3204 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
3205 {
3206     ImGuiContext& g = *GImGui;
3207 
3208     // Increment counters
3209     const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3210     window->DC.FocusCounterRegular++;
3211     if (is_tab_stop)
3212         window->DC.FocusCounterTabStop++;
3213 
3214     // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3215     // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3216     if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
3217     {
3218         g.FocusRequestNextWindow = window;
3219         g.FocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (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.
3220     }
3221 
3222     // Handle focus requests
3223     if (g.FocusRequestCurrWindow == window)
3224     {
3225         if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular)
3226             return true;
3227         if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop)
3228         {
3229             g.NavJustTabbedId = id;
3230             return true;
3231         }
3232 
3233         // If another item is about to be focused, we clear our own active id
3234         if (g.ActiveId == id)
3235             ClearActiveID();
3236     }
3237 
3238     return false;
3239 }
3240 
FocusableItemUnregister(ImGuiWindow * window)3241 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3242 {
3243     window->DC.FocusCounterRegular--;
3244     window->DC.FocusCounterTabStop--;
3245 }
3246 
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3247 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3248 {
3249     if (wrap_pos_x < 0.0f)
3250         return 0.0f;
3251 
3252     ImGuiContext& g = *GImGui;
3253     ImGuiWindow* window = g.CurrentWindow;
3254     if (wrap_pos_x == 0.0f)
3255     {
3256         // We could decide to setup a default wrapping max point for auto-resizing windows,
3257         // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3258         //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3259         //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3260         //else
3261         wrap_pos_x = window->WorkRect.Max.x;
3262     }
3263     else if (wrap_pos_x > 0.0f)
3264     {
3265         wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3266     }
3267 
3268     return ImMax(wrap_pos_x - pos.x, 1.0f);
3269 }
3270 
3271 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3272 void* ImGui::MemAlloc(size_t size)
3273 {
3274     if (ImGuiContext* ctx = GImGui)
3275         ctx->IO.MetricsActiveAllocations++;
3276     return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3277 }
3278 
3279 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3280 void ImGui::MemFree(void* ptr)
3281 {
3282     if (ptr)
3283         if (ImGuiContext* ctx = GImGui)
3284             ctx->IO.MetricsActiveAllocations--;
3285     return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3286 }
3287 
GetClipboardText()3288 const char* ImGui::GetClipboardText()
3289 {
3290     ImGuiContext& g = *GImGui;
3291     return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3292 }
3293 
SetClipboardText(const char * text)3294 void ImGui::SetClipboardText(const char* text)
3295 {
3296     ImGuiContext& g = *GImGui;
3297     if (g.IO.SetClipboardTextFn)
3298         g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3299 }
3300 
GetVersion()3301 const char* ImGui::GetVersion()
3302 {
3303     return IMGUI_VERSION;
3304 }
3305 
3306 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3307 // 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()3308 ImGuiContext* ImGui::GetCurrentContext()
3309 {
3310     return GImGui;
3311 }
3312 
SetCurrentContext(ImGuiContext * ctx)3313 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3314 {
3315 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3316     IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3317 #else
3318     GImGui = ctx;
3319 #endif
3320 }
3321 
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3322 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3323 {
3324     GImAllocatorAllocFunc = alloc_func;
3325     GImAllocatorFreeFunc = free_func;
3326     GImAllocatorUserData = user_data;
3327 }
3328 
CreateContext(ImFontAtlas * shared_font_atlas)3329 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3330 {
3331     ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3332     if (GImGui == NULL)
3333         SetCurrentContext(ctx);
3334     Initialize(ctx);
3335     return ctx;
3336 }
3337 
DestroyContext(ImGuiContext * ctx)3338 void ImGui::DestroyContext(ImGuiContext* ctx)
3339 {
3340     if (ctx == NULL)
3341         ctx = GImGui;
3342     Shutdown(ctx);
3343     if (GImGui == ctx)
3344         SetCurrentContext(NULL);
3345     IM_DELETE(ctx);
3346 }
3347 
3348 // No specific ordering/dependency support, will see as needed
AddContextHook(ImGuiContext * ctx,const ImGuiContextHook * hook)3349 void ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3350 {
3351     ImGuiContext& g = *ctx;
3352     IM_ASSERT(hook->Callback != NULL);
3353     g.Hooks.push_back(*hook);
3354 }
3355 
3356 // Call context hooks (used by e.g. test engine)
3357 // We assume a small number of hooks so all stored in same array
CallContextHooks(ImGuiContext * ctx,ImGuiContextHookType hook_type)3358 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3359 {
3360     ImGuiContext& g = *ctx;
3361     for (int n = 0; n < g.Hooks.Size; n++)
3362         if (g.Hooks[n].Type == hook_type)
3363             g.Hooks[n].Callback(&g, &g.Hooks[n]);
3364 }
3365 
GetIO()3366 ImGuiIO& ImGui::GetIO()
3367 {
3368     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3369     return GImGui->IO;
3370 }
3371 
3372 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
GetDrawData()3373 ImDrawData* ImGui::GetDrawData()
3374 {
3375     ImGuiContext& g = *GImGui;
3376     return g.DrawData.Valid ? &g.DrawData : NULL;
3377 }
3378 
GetTime()3379 double ImGui::GetTime()
3380 {
3381     return GImGui->Time;
3382 }
3383 
GetFrameCount()3384 int ImGui::GetFrameCount()
3385 {
3386     return GImGui->FrameCount;
3387 }
3388 
GetBackgroundDrawList()3389 ImDrawList* ImGui::GetBackgroundDrawList()
3390 {
3391     return &GImGui->BackgroundDrawList;
3392 }
3393 
GetForegroundDrawList()3394 ImDrawList* ImGui::GetForegroundDrawList()
3395 {
3396     return &GImGui->ForegroundDrawList;
3397 }
3398 
GetDrawListSharedData()3399 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3400 {
3401     return &GImGui->DrawListSharedData;
3402 }
3403 
StartMouseMovingWindow(ImGuiWindow * window)3404 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3405 {
3406     // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3407     // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3408     // This is because we want ActiveId to be set even when the window is not permitted to move.
3409     ImGuiContext& g = *GImGui;
3410     FocusWindow(window);
3411     SetActiveID(window->MoveId, window);
3412     g.NavDisableHighlight = true;
3413     g.ActiveIdNoClearOnFocusLoss = true;
3414     g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3415 
3416     bool can_move_window = true;
3417     if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3418         can_move_window = false;
3419     if (can_move_window)
3420         g.MovingWindow = window;
3421 }
3422 
3423 // Handle mouse moving window
3424 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3425 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3426 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3427 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3428 void ImGui::UpdateMouseMovingWindowNewFrame()
3429 {
3430     ImGuiContext& g = *GImGui;
3431     if (g.MovingWindow != NULL)
3432     {
3433         // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3434         // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3435         KeepAliveID(g.ActiveId);
3436         IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3437         ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3438         if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3439         {
3440             ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3441             if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3442             {
3443                 MarkIniSettingsDirty(moving_window);
3444                 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3445             }
3446             FocusWindow(g.MovingWindow);
3447         }
3448         else
3449         {
3450             ClearActiveID();
3451             g.MovingWindow = NULL;
3452         }
3453     }
3454     else
3455     {
3456         // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3457         if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3458         {
3459             KeepAliveID(g.ActiveId);
3460             if (!g.IO.MouseDown[0])
3461                 ClearActiveID();
3462         }
3463     }
3464 }
3465 
3466 // Initiate moving window when clicking on empty space or title bar.
3467 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3468 void ImGui::UpdateMouseMovingWindowEndFrame()
3469 {
3470     ImGuiContext& g = *GImGui;
3471     if (g.ActiveId != 0 || g.HoveredId != 0)
3472         return;
3473 
3474     // Unless we just made a window/popup appear
3475     if (g.NavWindow && g.NavWindow->Appearing)
3476         return;
3477 
3478     // Click on empty space to focus window and start moving (after we're done with all our widgets)
3479     if (g.IO.MouseClicked[0])
3480     {
3481         // Handle the edge case of a popup being closed while clicking in its empty space.
3482         // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3483         ImGuiWindow* root_window = g.HoveredRootWindow;
3484         const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3485 
3486         if (root_window != NULL && !is_closed_popup)
3487         {
3488             StartMouseMovingWindow(g.HoveredWindow); //-V595
3489 
3490             // Cancel moving if clicked outside of title bar
3491             if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
3492                 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3493                     g.MovingWindow = NULL;
3494 
3495             // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3496             if (g.HoveredIdDisabled)
3497                 g.MovingWindow = NULL;
3498         }
3499         else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3500         {
3501             // Clicking on void disable focus
3502             FocusWindow(NULL);
3503         }
3504     }
3505 
3506     // With right mouse button we close popups without changing focus based on where the mouse is aimed
3507     // Instead, focus will be restored to the window under the bottom-most closed popup.
3508     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3509     if (g.IO.MouseClicked[1])
3510     {
3511         // Find the top-most window between HoveredWindow and the top-most Modal Window.
3512         // This is where we can trim the popup stack.
3513         ImGuiWindow* modal = GetTopMostPopupModal();
3514         bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal);
3515         ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3516     }
3517 }
3518 
IsWindowActiveAndVisible(ImGuiWindow * window)3519 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3520 {
3521     return (window->Active) && (!window->Hidden);
3522 }
3523 
UpdateMouseInputs()3524 static void ImGui::UpdateMouseInputs()
3525 {
3526     ImGuiContext& g = *GImGui;
3527 
3528     // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3529     if (IsMousePosValid(&g.IO.MousePos))
3530         g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3531 
3532     // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3533     if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3534         g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3535     else
3536         g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3537     if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3538         g.NavDisableMouseHover = false;
3539 
3540     g.IO.MousePosPrev = g.IO.MousePos;
3541     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3542     {
3543         g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3544         g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3545         g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3546         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;
3547         g.IO.MouseDoubleClicked[i] = false;
3548         if (g.IO.MouseClicked[i])
3549         {
3550             if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3551             {
3552                 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3553                 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3554                     g.IO.MouseDoubleClicked[i] = true;
3555                 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3556             }
3557             else
3558             {
3559                 g.IO.MouseClickedTime[i] = g.Time;
3560             }
3561             g.IO.MouseClickedPos[i] = g.IO.MousePos;
3562             g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3563             g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3564             g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3565         }
3566         else if (g.IO.MouseDown[i])
3567         {
3568             // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3569             ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3570             g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3571             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);
3572             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);
3573         }
3574         if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3575             g.IO.MouseDownWasDoubleClick[i] = false;
3576         if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3577             g.NavDisableMouseHover = false;
3578     }
3579 }
3580 
StartLockWheelingWindow(ImGuiWindow * window)3581 static void StartLockWheelingWindow(ImGuiWindow* window)
3582 {
3583     ImGuiContext& g = *GImGui;
3584     if (g.WheelingWindow == window)
3585         return;
3586     g.WheelingWindow = window;
3587     g.WheelingWindowRefMousePos = g.IO.MousePos;
3588     g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3589 }
3590 
UpdateMouseWheel()3591 void ImGui::UpdateMouseWheel()
3592 {
3593     ImGuiContext& g = *GImGui;
3594 
3595     // Reset the locked window if we move the mouse or after the timer elapses
3596     if (g.WheelingWindow != NULL)
3597     {
3598         g.WheelingWindowTimer -= g.IO.DeltaTime;
3599         if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3600             g.WheelingWindowTimer = 0.0f;
3601         if (g.WheelingWindowTimer <= 0.0f)
3602         {
3603             g.WheelingWindow = NULL;
3604             g.WheelingWindowTimer = 0.0f;
3605         }
3606     }
3607 
3608     if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3609         return;
3610 
3611     if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel))
3612         return;
3613 
3614     ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3615     if (!window || window->Collapsed)
3616         return;
3617 
3618     // Zoom / Scale window
3619     // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3620     if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3621     {
3622         StartLockWheelingWindow(window);
3623         const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3624         const float scale = new_font_scale / window->FontWindowScale;
3625         window->FontWindowScale = new_font_scale;
3626         if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3627         {
3628             const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3629             SetWindowPos(window, window->Pos + offset, 0);
3630             window->Size = ImFloor(window->Size * scale);
3631             window->SizeFull = ImFloor(window->SizeFull * scale);
3632         }
3633         return;
3634     }
3635 
3636     // Mouse wheel scrolling
3637     // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3638 
3639     // Vertical Mouse Wheel scrolling
3640     const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3641     if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3642     {
3643         StartLockWheelingWindow(window);
3644         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3645             window = window->ParentWindow;
3646         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3647         {
3648             float max_step = window->InnerRect.GetHeight() * 0.67f;
3649             float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3650             SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3651         }
3652     }
3653 
3654     // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3655     const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3656     if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3657     {
3658         StartLockWheelingWindow(window);
3659         while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3660             window = window->ParentWindow;
3661         if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3662         {
3663             float max_step = window->InnerRect.GetWidth() * 0.67f;
3664             float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3665             SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3666         }
3667     }
3668 }
3669 
UpdateTabFocus()3670 void ImGui::UpdateTabFocus()
3671 {
3672     ImGuiContext& g = *GImGui;
3673 
3674     // Pressing TAB activate widget focus
3675     g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3676     if (g.ActiveId == 0 && g.FocusTabPressed)
3677     {
3678         // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3679         // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3680         g.FocusRequestNextWindow = g.NavWindow;
3681         g.FocusRequestNextCounterRegular = INT_MAX;
3682         if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3683             g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3684         else
3685             g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3686     }
3687 
3688     // Turn queued focus request into current one
3689     g.FocusRequestCurrWindow = NULL;
3690     g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX;
3691     if (g.FocusRequestNextWindow != NULL)
3692     {
3693         ImGuiWindow* window = g.FocusRequestNextWindow;
3694         g.FocusRequestCurrWindow = window;
3695         if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3696             g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3697         if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3698             g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3699         g.FocusRequestNextWindow = NULL;
3700         g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX;
3701     }
3702 
3703     g.NavIdTabCounter = INT_MAX;
3704 }
3705 
3706 // 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()3707 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3708 {
3709     ImGuiContext& g = *GImGui;
3710 
3711     // Find the window hovered by mouse:
3712     // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3713     // - 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.
3714     // - 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.
3715     bool clear_hovered_windows = false;
3716     FindHoveredWindow();
3717 
3718     // Modal windows prevents mouse from hovering behind them.
3719     ImGuiWindow* modal_window = GetTopMostPopupModal();
3720     if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3721         clear_hovered_windows = true;
3722 
3723     // Disabled mouse?
3724     if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3725         clear_hovered_windows = true;
3726 
3727     // 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.
3728     int mouse_earliest_button_down = -1;
3729     bool mouse_any_down = false;
3730     for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3731     {
3732         if (g.IO.MouseClicked[i])
3733             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
3734         mouse_any_down |= g.IO.MouseDown[i];
3735         if (g.IO.MouseDown[i])
3736             if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3737                 mouse_earliest_button_down = i;
3738     }
3739     const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3740 
3741     // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3742     // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3743     const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3744     if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3745         clear_hovered_windows = true;
3746 
3747     if (clear_hovered_windows)
3748         g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
3749 
3750     // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3751     if (g.WantCaptureMouseNextFrame != -1)
3752         g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3753     else
3754         g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
3755 
3756     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3757     if (g.WantCaptureKeyboardNextFrame != -1)
3758         g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3759     else
3760         g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3761     if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3762         g.IO.WantCaptureKeyboard = true;
3763 
3764     // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3765     g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3766 }
3767 
GetMergedKeyModFlags()3768 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3769 {
3770     ImGuiContext& g = *GImGui;
3771     ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3772     if (g.IO.KeyCtrl)   { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3773     if (g.IO.KeyShift)  { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3774     if (g.IO.KeyAlt)    { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3775     if (g.IO.KeySuper)  { key_mod_flags |= ImGuiKeyModFlags_Super; }
3776     return key_mod_flags;
3777 }
3778 
NewFrame()3779 void ImGui::NewFrame()
3780 {
3781     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3782     ImGuiContext& g = *GImGui;
3783 
3784     CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
3785 
3786     // Check and assert for various common IO and Configuration mistakes
3787     ErrorCheckNewFrameSanityChecks();
3788 
3789     // Load settings on first frame, save settings when modified (after a delay)
3790     UpdateSettings();
3791 
3792     g.Time += g.IO.DeltaTime;
3793     g.WithinFrameScope = true;
3794     g.FrameCount += 1;
3795     g.TooltipOverrideCount = 0;
3796     g.WindowsActiveCount = 0;
3797     g.MenusIdSubmittedThisFrame.resize(0);
3798 
3799     // Calculate frame-rate for the user, as a purely luxurious feature
3800     g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3801     g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3802     g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3803     g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3804 
3805     // Setup current font and draw list shared data
3806     g.IO.Fonts->Locked = true;
3807     SetCurrentFont(GetDefaultFont());
3808     IM_ASSERT(g.Font->IsLoaded());
3809     g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3810     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3811     g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
3812     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3813     if (g.Style.AntiAliasedLines)
3814         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3815     if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
3816         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
3817     if (g.Style.AntiAliasedFill)
3818         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3819     if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3820         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3821 
3822     g.BackgroundDrawList._ResetForNewFrame();
3823     g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3824     g.BackgroundDrawList.PushClipRectFullScreen();
3825 
3826     g.ForegroundDrawList._ResetForNewFrame();
3827     g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3828     g.ForegroundDrawList.PushClipRectFullScreen();
3829 
3830     // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3831     g.DrawData.Clear();
3832 
3833     // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3834     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3835         KeepAliveID(g.DragDropPayload.SourceId);
3836 
3837     // Update HoveredId data
3838     if (!g.HoveredIdPreviousFrame)
3839         g.HoveredIdTimer = 0.0f;
3840     if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3841         g.HoveredIdNotActiveTimer = 0.0f;
3842     if (g.HoveredId)
3843         g.HoveredIdTimer += g.IO.DeltaTime;
3844     if (g.HoveredId && g.ActiveId != g.HoveredId)
3845         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3846     g.HoveredIdPreviousFrame = g.HoveredId;
3847     g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
3848     g.HoveredId = 0;
3849     g.HoveredIdAllowOverlap = false;
3850     g.HoveredIdUsingMouseWheel = false;
3851     g.HoveredIdDisabled = false;
3852 
3853     // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
3854     if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3855         ClearActiveID();
3856     if (g.ActiveId)
3857         g.ActiveIdTimer += g.IO.DeltaTime;
3858     g.LastActiveIdTimer += g.IO.DeltaTime;
3859     g.ActiveIdPreviousFrame = g.ActiveId;
3860     g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3861     g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3862     g.ActiveIdIsAlive = 0;
3863     g.ActiveIdHasBeenEditedThisFrame = false;
3864     g.ActiveIdPreviousFrameIsAlive = false;
3865     g.ActiveIdIsJustActivated = false;
3866     if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
3867         g.TempInputId = 0;
3868     if (g.ActiveId == 0)
3869     {
3870         g.ActiveIdUsingNavDirMask = 0x00;
3871         g.ActiveIdUsingNavInputMask = 0x00;
3872         g.ActiveIdUsingKeyInputMask = 0x00;
3873     }
3874 
3875     // Drag and drop
3876     g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3877     g.DragDropAcceptIdCurr = 0;
3878     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3879     g.DragDropWithinSource = false;
3880     g.DragDropWithinTarget = false;
3881     g.DragDropHoldJustPressedId = 0;
3882 
3883     // Update keyboard input state
3884     // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
3885     g.IO.KeyMods = GetMergedKeyModFlags();
3886     memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3887     for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3888         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;
3889 
3890     // Update gamepad/keyboard navigation
3891     NavUpdate();
3892 
3893     // Update mouse input state
3894     UpdateMouseInputs();
3895 
3896     // Find hovered window
3897     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3898     UpdateHoveredWindowAndCaptureFlags();
3899 
3900     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3901     UpdateMouseMovingWindowNewFrame();
3902 
3903     // Background darkening/whitening
3904     if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3905         g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3906     else
3907         g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3908 
3909     g.MouseCursor = ImGuiMouseCursor_Arrow;
3910     g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3911     g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3912 
3913     // Mouse wheel scrolling, scale
3914     UpdateMouseWheel();
3915 
3916     // Update legacy TAB focus
3917     UpdateTabFocus();
3918 
3919     // Mark all windows as not visible and compact unused memory.
3920     IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3921     const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
3922     for (int i = 0; i != g.Windows.Size; i++)
3923     {
3924         ImGuiWindow* window = g.Windows[i];
3925         window->WasActive = window->Active;
3926         window->BeginCount = 0;
3927         window->Active = false;
3928         window->WriteAccessed = false;
3929 
3930         // Garbage collect transient buffers of recently unused windows
3931         if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3932             GcCompactTransientWindowBuffers(window);
3933     }
3934 
3935     // Garbage collect transient buffers of recently unused tables
3936     for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
3937         if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
3938             TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
3939     if (g.GcCompactAll)
3940         GcCompactTransientMiscBuffers();
3941     g.GcCompactAll = false;
3942 
3943     // Closing the focused window restore focus to the first active root window in descending z-order
3944     if (g.NavWindow && !g.NavWindow->WasActive)
3945         FocusTopMostWindowUnderOne(NULL, NULL);
3946 
3947     // No window should be open at the beginning of the frame.
3948     // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3949     g.CurrentWindowStack.resize(0);
3950     g.BeginPopupStack.resize(0);
3951     g.ItemFlagsStack.resize(0);
3952     g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_);
3953     g.GroupStack.resize(0);
3954     ClosePopupsOverWindow(g.NavWindow, false);
3955 
3956     // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3957     UpdateDebugToolItemPicker();
3958 
3959     // Create implicit/fallback window - which we will only render it if the user has added something to it.
3960     // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3961     // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3962     g.WithinFrameScopeWithImplicitWindow = true;
3963     SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
3964     Begin("Debug##Default");
3965     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
3966 
3967     CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
3968 }
3969 
3970 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3971 void ImGui::UpdateDebugToolItemPicker()
3972 {
3973     ImGuiContext& g = *GImGui;
3974     g.DebugItemPickerBreakId = 0;
3975     if (g.DebugItemPickerActive)
3976     {
3977         const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3978         ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3979         if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3980             g.DebugItemPickerActive = false;
3981         if (ImGui::IsMouseClicked(0) && hovered_id)
3982         {
3983             g.DebugItemPickerBreakId = hovered_id;
3984             g.DebugItemPickerActive = false;
3985         }
3986         ImGui::SetNextWindowBgAlpha(0.60f);
3987         ImGui::BeginTooltip();
3988         ImGui::Text("HoveredId: 0x%08X", hovered_id);
3989         ImGui::Text("Press ESC to abort picking.");
3990         ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3991         ImGui::EndTooltip();
3992     }
3993 }
3994 
Initialize(ImGuiContext * context)3995 void ImGui::Initialize(ImGuiContext* context)
3996 {
3997     ImGuiContext& g = *context;
3998     IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3999 
4000     // Add .ini handle for ImGuiWindow type
4001     {
4002         ImGuiSettingsHandler ini_handler;
4003         ini_handler.TypeName = "Window";
4004         ini_handler.TypeHash = ImHashStr("Window");
4005         ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4006         ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4007         ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4008         ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4009         ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4010         g.SettingsHandlers.push_back(ini_handler);
4011     }
4012 
4013 #ifdef IMGUI_HAS_TABLE
4014     // Add .ini handle for ImGuiTable type
4015     TableSettingsInstallHandler(context);
4016 #endif // #ifdef IMGUI_HAS_TABLE
4017 
4018 #ifdef IMGUI_HAS_DOCK
4019 #endif // #ifdef IMGUI_HAS_DOCK
4020 
4021     g.Initialized = true;
4022 }
4023 
4024 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)4025 void ImGui::Shutdown(ImGuiContext* context)
4026 {
4027     // 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)
4028     ImGuiContext& g = *context;
4029     if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4030     {
4031         g.IO.Fonts->Locked = false;
4032         IM_DELETE(g.IO.Fonts);
4033     }
4034     g.IO.Fonts = NULL;
4035 
4036     // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4037     if (!g.Initialized)
4038         return;
4039 
4040     // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4041     if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4042     {
4043         ImGuiContext* backup_context = GImGui;
4044         SetCurrentContext(&g);
4045         SaveIniSettingsToDisk(g.IO.IniFilename);
4046         SetCurrentContext(backup_context);
4047     }
4048 
4049     CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4050 
4051     // Clear everything else
4052     for (int i = 0; i < g.Windows.Size; i++)
4053         IM_DELETE(g.Windows[i]);
4054     g.Windows.clear();
4055     g.WindowsFocusOrder.clear();
4056     g.WindowsTempSortBuffer.clear();
4057     g.CurrentWindow = NULL;
4058     g.CurrentWindowStack.clear();
4059     g.WindowsById.Clear();
4060     g.NavWindow = NULL;
4061     g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
4062     g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4063     g.MovingWindow = NULL;
4064     g.ColorStack.clear();
4065     g.StyleVarStack.clear();
4066     g.FontStack.clear();
4067     g.OpenPopupStack.clear();
4068     g.BeginPopupStack.clear();
4069     g.DrawDataBuilder.ClearFreeMemory();
4070     g.BackgroundDrawList._ClearFreeMemory();
4071     g.ForegroundDrawList._ClearFreeMemory();
4072 
4073     g.TabBars.Clear();
4074     g.CurrentTabBarStack.clear();
4075     g.ShrinkWidthBuffer.clear();
4076 
4077     g.Tables.Clear();
4078     g.CurrentTableStack.clear();
4079     g.DrawChannelsTempMergeBuffer.clear();
4080 
4081     g.ClipboardHandlerData.clear();
4082     g.MenusIdSubmittedThisFrame.clear();
4083     g.InputTextState.ClearFreeMemory();
4084 
4085     g.SettingsWindows.clear();
4086     g.SettingsHandlers.clear();
4087 
4088     if (g.LogFile)
4089     {
4090 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4091         if (g.LogFile != stdout)
4092 #endif
4093             ImFileClose(g.LogFile);
4094         g.LogFile = NULL;
4095     }
4096     g.LogBuffer.clear();
4097 
4098     g.Initialized = false;
4099 }
4100 
4101 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4102 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4103 {
4104     const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4105     const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4106     if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4107         return d;
4108     if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4109         return d;
4110     return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4111 }
4112 
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4113 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4114 {
4115     out_sorted_windows->push_back(window);
4116     if (window->Active)
4117     {
4118         int count = window->DC.ChildWindows.Size;
4119         if (count > 1)
4120             ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4121         for (int i = 0; i < count; i++)
4122         {
4123             ImGuiWindow* child = window->DC.ChildWindows[i];
4124             if (child->Active)
4125                 AddWindowToSortBuffer(out_sorted_windows, child);
4126         }
4127     }
4128 }
4129 
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4130 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4131 {
4132     // Remove trailing command if unused.
4133     // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well.
4134     draw_list->_PopUnusedDrawCmd();
4135     if (draw_list->CmdBuffer.Size == 0)
4136         return;
4137 
4138     // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4139     // May trigger for you if you are using PrimXXX functions incorrectly.
4140     IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4141     IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4142     if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4143         IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4144 
4145     // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4146     // If this assert triggers because you are drawing lots of stuff manually:
4147     // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4148     //   Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
4149     // - If you want large meshes with more than 64K vertices, you can either:
4150     //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4151     //       Most example backends already support this from 1.71. Pre-1.71 backends won't.
4152     //       Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
4153     //   (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4154     //       Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
4155     //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4156     //       Your own engine or render API may use different parameters or function calls to specify index sizes.
4157     //       2 and 4 bytes indices are generally supported by most graphics API.
4158     // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4159     //   the 64K limit to split your draw commands in multiple draw lists.
4160     if (sizeof(ImDrawIdx) == 2)
4161         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4162 
4163     out_list->push_back(draw_list);
4164 }
4165 
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)4166 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
4167 {
4168     ImGuiContext& g = *GImGui;
4169     g.IO.MetricsRenderWindows++;
4170     AddDrawListToDrawData(out_render_list, window->DrawList);
4171     for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4172     {
4173         ImGuiWindow* child = window->DC.ChildWindows[i];
4174         if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
4175             AddWindowToDrawData(out_render_list, child);
4176     }
4177 }
4178 
4179 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4180 static void AddRootWindowToDrawData(ImGuiWindow* window)
4181 {
4182     ImGuiContext& g = *GImGui;
4183     int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4184     AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window);
4185 }
4186 
FlattenIntoSingleLayer()4187 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4188 {
4189     int n = Layers[0].Size;
4190     int size = n;
4191     for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4192         size += Layers[i].Size;
4193     Layers[0].resize(size);
4194     for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4195     {
4196         ImVector<ImDrawList*>& layer = Layers[layer_n];
4197         if (layer.empty())
4198             continue;
4199         memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4200         n += layer.Size;
4201         layer.resize(0);
4202     }
4203 }
4204 
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)4205 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
4206 {
4207     ImGuiIO& io = ImGui::GetIO();
4208     draw_data->Valid = true;
4209     draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4210     draw_data->CmdListsCount = draw_lists->Size;
4211     draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4212     draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
4213     draw_data->DisplaySize = io.DisplaySize;
4214     draw_data->FramebufferScale = io.DisplayFramebufferScale;
4215     for (int n = 0; n < draw_lists->Size; n++)
4216     {
4217         draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4218         draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4219     }
4220 }
4221 
4222 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4223 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4224 //   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4225 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4226 //   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4227 //   more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)4228 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4229 {
4230     ImGuiWindow* window = GetCurrentWindow();
4231     window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4232     window->ClipRect = window->DrawList->_ClipRectStack.back();
4233 }
4234 
PopClipRect()4235 void ImGui::PopClipRect()
4236 {
4237     ImGuiWindow* window = GetCurrentWindow();
4238     window->DrawList->PopClipRect();
4239     window->ClipRect = window->DrawList->_ClipRectStack.back();
4240 }
4241 
4242 // 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()4243 void ImGui::EndFrame()
4244 {
4245     ImGuiContext& g = *GImGui;
4246     IM_ASSERT(g.Initialized);
4247 
4248     // Don't process EndFrame() multiple times.
4249     if (g.FrameCountEnded == g.FrameCount)
4250         return;
4251     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4252 
4253     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
4254 
4255     ErrorCheckEndFrameSanityChecks();
4256 
4257     // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4258     if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4259     {
4260         g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4261         g.PlatformImeLastPos = g.PlatformImePos;
4262     }
4263 
4264     // Hide implicit/fallback "Debug" window if it hasn't been used
4265     g.WithinFrameScopeWithImplicitWindow = false;
4266     if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4267         g.CurrentWindow->Active = false;
4268     End();
4269 
4270     // Update navigation: CTRL+Tab, wrap-around requests
4271     NavEndFrame();
4272 
4273     // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4274     if (g.DragDropActive)
4275     {
4276         bool is_delivered = g.DragDropPayload.Delivery;
4277         bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4278         if (is_delivered || is_elapsed)
4279             ClearDragDrop();
4280     }
4281 
4282     // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4283     if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4284     {
4285         g.DragDropWithinSource = true;
4286         SetTooltip("...");
4287         g.DragDropWithinSource = false;
4288     }
4289 
4290     // End frame
4291     g.WithinFrameScope = false;
4292     g.FrameCountEnded = g.FrameCount;
4293 
4294     // Initiate moving window + handle left-click and right-click focus
4295     UpdateMouseMovingWindowEndFrame();
4296 
4297     // Sort the window list so that all child windows are after their parent
4298     // We cannot do that on FocusWindow() because children may not exist yet
4299     g.WindowsTempSortBuffer.resize(0);
4300     g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4301     for (int i = 0; i != g.Windows.Size; i++)
4302     {
4303         ImGuiWindow* window = g.Windows[i];
4304         if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
4305             continue;
4306         AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4307     }
4308 
4309     // 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.
4310     IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4311     g.Windows.swap(g.WindowsTempSortBuffer);
4312     g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4313 
4314     // Unlock font atlas
4315     g.IO.Fonts->Locked = false;
4316 
4317     // Clear Input data for next frame
4318     g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4319     g.IO.InputQueueCharacters.resize(0);
4320     memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4321 
4322     CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
4323 }
4324 
Render()4325 void ImGui::Render()
4326 {
4327     ImGuiContext& g = *GImGui;
4328     IM_ASSERT(g.Initialized);
4329 
4330     if (g.FrameCountEnded != g.FrameCount)
4331         EndFrame();
4332     g.FrameCountRendered = g.FrameCount;
4333     g.IO.MetricsRenderWindows = 0;
4334     g.DrawDataBuilder.Clear();
4335 
4336     CallContextHooks(&g, ImGuiContextHookType_RenderPre);
4337 
4338     // Add background ImDrawList
4339     if (!g.BackgroundDrawList.VtxBuffer.empty())
4340         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4341 
4342     // Add ImDrawList to render
4343     ImGuiWindow* windows_to_render_top_most[2];
4344     windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4345     windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4346     for (int n = 0; n != g.Windows.Size; n++)
4347     {
4348         ImGuiWindow* window = g.Windows[n];
4349         if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4350             AddRootWindowToDrawData(window);
4351     }
4352     for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4353         if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4354             AddRootWindowToDrawData(windows_to_render_top_most[n]);
4355     g.DrawDataBuilder.FlattenIntoSingleLayer();
4356 
4357     // Draw software mouse cursor if requested
4358     if (g.IO.MouseDrawCursor)
4359         RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4360 
4361     // Add foreground ImDrawList
4362     if (!g.ForegroundDrawList.VtxBuffer.empty())
4363         AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4364 
4365     // Setup ImDrawData structure for end-user
4366     SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4367     g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4368     g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4369 
4370     CallContextHooks(&g, ImGuiContextHookType_RenderPost);
4371 }
4372 
4373 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4374 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)4375 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4376 {
4377     ImGuiContext& g = *GImGui;
4378 
4379     const char* text_display_end;
4380     if (hide_text_after_double_hash)
4381         text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
4382     else
4383         text_display_end = text_end;
4384 
4385     ImFont* font = g.Font;
4386     const float font_size = g.FontSize;
4387     if (text == text_display_end)
4388         return ImVec2(0.0f, font_size);
4389     ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4390 
4391     // Round
4392     text_size.x = IM_FLOOR(text_size.x + 0.95f);
4393 
4394     return text_size;
4395 }
4396 
4397 // Find window given position, search front-to-back
4398 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4399 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4400 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4401 static void FindHoveredWindow()
4402 {
4403     ImGuiContext& g = *GImGui;
4404 
4405     ImGuiWindow* hovered_window = NULL;
4406     ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4407     if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4408         hovered_window = g.MovingWindow;
4409 
4410     ImVec2 padding_regular = g.Style.TouchExtraPadding;
4411     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;
4412     for (int i = g.Windows.Size - 1; i >= 0; i--)
4413     {
4414         ImGuiWindow* window = g.Windows[i];
4415         if (!window->Active || window->Hidden)
4416             continue;
4417         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4418             continue;
4419 
4420         // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4421         ImRect bb(window->OuterRectClipped);
4422         if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4423             bb.Expand(padding_regular);
4424         else
4425             bb.Expand(padding_for_resize_from_edges);
4426         if (!bb.Contains(g.IO.MousePos))
4427             continue;
4428 
4429         // Support for one rectangular hole in any given window
4430         // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4431         if (window->HitTestHoleSize.x != 0)
4432         {
4433             ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4434             ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4435             if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4436                 continue;
4437         }
4438 
4439         if (hovered_window == NULL)
4440             hovered_window = window;
4441         if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4442             hovered_window_ignoring_moving_window = window;
4443         if (hovered_window && hovered_window_ignoring_moving_window)
4444             break;
4445     }
4446 
4447     g.HoveredWindow = hovered_window;
4448     g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4449     g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4450 }
4451 
4452 // Test if mouse cursor is hovering given rectangle
4453 // NB- Rectangle is clipped by our current clip setting
4454 // 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)4455 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4456 {
4457     ImGuiContext& g = *GImGui;
4458 
4459     // Clip
4460     ImRect rect_clipped(r_min, r_max);
4461     if (clip)
4462         rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4463 
4464     // Expand for touch input
4465     const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4466     if (!rect_for_touch.Contains(g.IO.MousePos))
4467         return false;
4468     return true;
4469 }
4470 
GetKeyIndex(ImGuiKey imgui_key)4471 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4472 {
4473     IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4474     ImGuiContext& g = *GImGui;
4475     return g.IO.KeyMap[imgui_key];
4476 }
4477 
4478 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4479 // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4480 bool ImGui::IsKeyDown(int user_key_index)
4481 {
4482     if (user_key_index < 0)
4483         return false;
4484     ImGuiContext& g = *GImGui;
4485     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4486     return g.IO.KeysDown[user_key_index];
4487 }
4488 
4489 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4490 // t1 = current time (e.g.: g.Time)
4491 // An event is triggered at:
4492 //  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4493 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4494 {
4495     if (t1 == 0.0f)
4496         return 1;
4497     if (t0 >= t1)
4498         return 0;
4499     if (repeat_rate <= 0.0f)
4500         return (t0 < repeat_delay) && (t1 >= repeat_delay);
4501     const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4502     const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4503     const int count = count_t1 - count_t0;
4504     return count;
4505 }
4506 
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4507 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4508 {
4509     ImGuiContext& g = *GImGui;
4510     if (key_index < 0)
4511         return 0;
4512     IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4513     const float t = g.IO.KeysDownDuration[key_index];
4514     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4515 }
4516 
IsKeyPressed(int user_key_index,bool repeat)4517 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4518 {
4519     ImGuiContext& g = *GImGui;
4520     if (user_key_index < 0)
4521         return false;
4522     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4523     const float t = g.IO.KeysDownDuration[user_key_index];
4524     if (t == 0.0f)
4525         return true;
4526     if (repeat && t > g.IO.KeyRepeatDelay)
4527         return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4528     return false;
4529 }
4530 
IsKeyReleased(int user_key_index)4531 bool ImGui::IsKeyReleased(int user_key_index)
4532 {
4533     ImGuiContext& g = *GImGui;
4534     if (user_key_index < 0) return false;
4535     IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4536     return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4537 }
4538 
IsMouseDown(ImGuiMouseButton button)4539 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4540 {
4541     ImGuiContext& g = *GImGui;
4542     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4543     return g.IO.MouseDown[button];
4544 }
4545 
IsMouseClicked(ImGuiMouseButton button,bool repeat)4546 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4547 {
4548     ImGuiContext& g = *GImGui;
4549     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4550     const float t = g.IO.MouseDownDuration[button];
4551     if (t == 0.0f)
4552         return true;
4553 
4554     if (repeat && t > g.IO.KeyRepeatDelay)
4555     {
4556         // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold.
4557         int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4558         if (amount > 0)
4559             return true;
4560     }
4561     return false;
4562 }
4563 
IsMouseReleased(ImGuiMouseButton button)4564 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4565 {
4566     ImGuiContext& g = *GImGui;
4567     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4568     return g.IO.MouseReleased[button];
4569 }
4570 
IsMouseDoubleClicked(ImGuiMouseButton button)4571 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4572 {
4573     ImGuiContext& g = *GImGui;
4574     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4575     return g.IO.MouseDoubleClicked[button];
4576 }
4577 
4578 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4579 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4580 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4581 {
4582     ImGuiContext& g = *GImGui;
4583     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4584     if (lock_threshold < 0.0f)
4585         lock_threshold = g.IO.MouseDragThreshold;
4586     return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4587 }
4588 
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4589 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4590 {
4591     ImGuiContext& g = *GImGui;
4592     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4593     if (!g.IO.MouseDown[button])
4594         return false;
4595     return IsMouseDragPastThreshold(button, lock_threshold);
4596 }
4597 
GetMousePos()4598 ImVec2 ImGui::GetMousePos()
4599 {
4600     ImGuiContext& g = *GImGui;
4601     return g.IO.MousePos;
4602 }
4603 
4604 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4605 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4606 {
4607     ImGuiContext& g = *GImGui;
4608     if (g.BeginPopupStack.Size > 0)
4609         return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4610     return g.IO.MousePos;
4611 }
4612 
4613 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4614 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4615 {
4616     // The assert is only to silence a false-positive in XCode Static Analysis.
4617     // 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).
4618     IM_ASSERT(GImGui != NULL);
4619     const float MOUSE_INVALID = -256000.0f;
4620     ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4621     return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4622 }
4623 
IsAnyMouseDown()4624 bool ImGui::IsAnyMouseDown()
4625 {
4626     ImGuiContext& g = *GImGui;
4627     for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4628         if (g.IO.MouseDown[n])
4629             return true;
4630     return false;
4631 }
4632 
4633 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4634 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4635 // NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(ImGuiMouseButton button,float lock_threshold)4636 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4637 {
4638     ImGuiContext& g = *GImGui;
4639     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4640     if (lock_threshold < 0.0f)
4641         lock_threshold = g.IO.MouseDragThreshold;
4642     if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4643         if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4644             if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4645                 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4646     return ImVec2(0.0f, 0.0f);
4647 }
4648 
ResetMouseDragDelta(ImGuiMouseButton button)4649 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4650 {
4651     ImGuiContext& g = *GImGui;
4652     IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4653     // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4654     g.IO.MouseClickedPos[button] = g.IO.MousePos;
4655 }
4656 
GetMouseCursor()4657 ImGuiMouseCursor ImGui::GetMouseCursor()
4658 {
4659     return GImGui->MouseCursor;
4660 }
4661 
SetMouseCursor(ImGuiMouseCursor cursor_type)4662 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4663 {
4664     GImGui->MouseCursor = cursor_type;
4665 }
4666 
CaptureKeyboardFromApp(bool capture)4667 void ImGui::CaptureKeyboardFromApp(bool capture)
4668 {
4669     GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4670 }
4671 
CaptureMouseFromApp(bool capture)4672 void ImGui::CaptureMouseFromApp(bool capture)
4673 {
4674     GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4675 }
4676 
IsItemActive()4677 bool ImGui::IsItemActive()
4678 {
4679     ImGuiContext& g = *GImGui;
4680     if (g.ActiveId)
4681     {
4682         ImGuiWindow* window = g.CurrentWindow;
4683         return g.ActiveId == window->DC.LastItemId;
4684     }
4685     return false;
4686 }
4687 
IsItemActivated()4688 bool ImGui::IsItemActivated()
4689 {
4690     ImGuiContext& g = *GImGui;
4691     if (g.ActiveId)
4692     {
4693         ImGuiWindow* window = g.CurrentWindow;
4694         if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4695             return true;
4696     }
4697     return false;
4698 }
4699 
IsItemDeactivated()4700 bool ImGui::IsItemDeactivated()
4701 {
4702     ImGuiContext& g = *GImGui;
4703     ImGuiWindow* window = g.CurrentWindow;
4704     if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4705         return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4706     return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4707 }
4708 
IsItemDeactivatedAfterEdit()4709 bool ImGui::IsItemDeactivatedAfterEdit()
4710 {
4711     ImGuiContext& g = *GImGui;
4712     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4713 }
4714 
4715 // == GetItemID() == GetFocusID()
IsItemFocused()4716 bool ImGui::IsItemFocused()
4717 {
4718     ImGuiContext& g = *GImGui;
4719     ImGuiWindow* window = g.CurrentWindow;
4720 
4721     if (g.NavId != window->DC.LastItemId || g.NavId == 0)
4722         return false;
4723     return true;
4724 }
4725 
IsItemClicked(ImGuiMouseButton mouse_button)4726 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4727 {
4728     return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4729 }
4730 
IsItemToggledOpen()4731 bool ImGui::IsItemToggledOpen()
4732 {
4733     ImGuiContext& g = *GImGui;
4734     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4735 }
4736 
IsItemToggledSelection()4737 bool ImGui::IsItemToggledSelection()
4738 {
4739     ImGuiContext& g = *GImGui;
4740     return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4741 }
4742 
IsAnyItemHovered()4743 bool ImGui::IsAnyItemHovered()
4744 {
4745     ImGuiContext& g = *GImGui;
4746     return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4747 }
4748 
IsAnyItemActive()4749 bool ImGui::IsAnyItemActive()
4750 {
4751     ImGuiContext& g = *GImGui;
4752     return g.ActiveId != 0;
4753 }
4754 
IsAnyItemFocused()4755 bool ImGui::IsAnyItemFocused()
4756 {
4757     ImGuiContext& g = *GImGui;
4758     return g.NavId != 0 && !g.NavDisableHighlight;
4759 }
4760 
IsItemVisible()4761 bool ImGui::IsItemVisible()
4762 {
4763     ImGuiWindow* window = GetCurrentWindowRead();
4764     return window->ClipRect.Overlaps(window->DC.LastItemRect);
4765 }
4766 
IsItemEdited()4767 bool ImGui::IsItemEdited()
4768 {
4769     ImGuiWindow* window = GetCurrentWindowRead();
4770     return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4771 }
4772 
4773 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
4774 // FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
SetItemAllowOverlap()4775 void ImGui::SetItemAllowOverlap()
4776 {
4777     ImGuiContext& g = *GImGui;
4778     ImGuiID id = g.CurrentWindow->DC.LastItemId;
4779     if (g.HoveredId == id)
4780         g.HoveredIdAllowOverlap = true;
4781     if (g.ActiveId == id)
4782         g.ActiveIdAllowOverlap = true;
4783 }
4784 
SetItemUsingMouseWheel()4785 void ImGui::SetItemUsingMouseWheel()
4786 {
4787     ImGuiContext& g = *GImGui;
4788     ImGuiID id = g.CurrentWindow->DC.LastItemId;
4789     if (g.HoveredId == id)
4790         g.HoveredIdUsingMouseWheel = true;
4791     if (g.ActiveId == id)
4792         g.ActiveIdUsingMouseWheel = true;
4793 }
4794 
GetItemRectMin()4795 ImVec2 ImGui::GetItemRectMin()
4796 {
4797     ImGuiWindow* window = GetCurrentWindowRead();
4798     return window->DC.LastItemRect.Min;
4799 }
4800 
GetItemRectMax()4801 ImVec2 ImGui::GetItemRectMax()
4802 {
4803     ImGuiWindow* window = GetCurrentWindowRead();
4804     return window->DC.LastItemRect.Max;
4805 }
4806 
GetItemRectSize()4807 ImVec2 ImGui::GetItemRectSize()
4808 {
4809     ImGuiWindow* window = GetCurrentWindowRead();
4810     return window->DC.LastItemRect.GetSize();
4811 }
4812 
GetViewportRect()4813 static ImRect GetViewportRect()
4814 {
4815     ImGuiContext& g = *GImGui;
4816     return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4817 }
4818 
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4819 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4820 {
4821     ImGuiContext& g = *GImGui;
4822     ImGuiWindow* parent_window = g.CurrentWindow;
4823 
4824     flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
4825     flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove);  // Inherit the NoMove flag
4826 
4827     // Size
4828     const ImVec2 content_avail = GetContentRegionAvail();
4829     ImVec2 size = ImFloor(size_arg);
4830     const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4831     if (size.x <= 0.0f)
4832         size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4833     if (size.y <= 0.0f)
4834         size.y = ImMax(content_avail.y + size.y, 4.0f);
4835     SetNextWindowSize(size);
4836 
4837     // 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.
4838     if (name)
4839         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id);
4840     else
4841         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id);
4842 
4843     const float backup_border_size = g.Style.ChildBorderSize;
4844     if (!border)
4845         g.Style.ChildBorderSize = 0.0f;
4846     bool ret = Begin(g.TempBuffer, NULL, flags);
4847     g.Style.ChildBorderSize = backup_border_size;
4848 
4849     ImGuiWindow* child_window = g.CurrentWindow;
4850     child_window->ChildId = id;
4851     child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4852 
4853     // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4854     // While this is not really documented/defined, it seems that the expected thing to do.
4855     if (child_window->BeginCount == 1)
4856         parent_window->DC.CursorPos = child_window->Pos;
4857 
4858     // Process navigation-in immediately so NavInit can run on first frame
4859     if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4860     {
4861         FocusWindow(child_window);
4862         NavInitWindow(child_window, false);
4863         SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
4864         g.ActiveIdSource = ImGuiInputSource_Nav;
4865     }
4866     return ret;
4867 }
4868 
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4869 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4870 {
4871     ImGuiWindow* window = GetCurrentWindow();
4872     return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4873 }
4874 
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4875 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4876 {
4877     IM_ASSERT(id != 0);
4878     return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4879 }
4880 
EndChild()4881 void ImGui::EndChild()
4882 {
4883     ImGuiContext& g = *GImGui;
4884     ImGuiWindow* window = g.CurrentWindow;
4885 
4886     IM_ASSERT(g.WithinEndChild == false);
4887     IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
4888 
4889     g.WithinEndChild = true;
4890     if (window->BeginCount > 1)
4891     {
4892         End();
4893     }
4894     else
4895     {
4896         ImVec2 sz = window->Size;
4897         if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4898             sz.x = ImMax(4.0f, sz.x);
4899         if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4900             sz.y = ImMax(4.0f, sz.y);
4901         End();
4902 
4903         ImGuiWindow* parent_window = g.CurrentWindow;
4904         ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4905         ItemSize(sz);
4906         if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4907         {
4908             ItemAdd(bb, window->ChildId);
4909             RenderNavHighlight(bb, window->ChildId);
4910 
4911             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4912             if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4913                 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4914         }
4915         else
4916         {
4917             // Not navigable into
4918             ItemAdd(bb, 0);
4919         }
4920     }
4921     g.WithinEndChild = false;
4922 }
4923 
4924 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4925 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4926 {
4927     ImGuiContext& g = *GImGui;
4928     const ImGuiStyle& style = g.Style;
4929     PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4930     PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4931     PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4932     PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4933     bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4934     PopStyleVar(3);
4935     PopStyleColor();
4936     return ret;
4937 }
4938 
EndChildFrame()4939 void ImGui::EndChildFrame()
4940 {
4941     EndChild();
4942 }
4943 
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4944 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4945 {
4946     window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
4947     window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
4948     window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4949 }
4950 
FindWindowByID(ImGuiID id)4951 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4952 {
4953     ImGuiContext& g = *GImGui;
4954     return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4955 }
4956 
FindWindowByName(const char * name)4957 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4958 {
4959     ImGuiID id = ImHashStr(name);
4960     return FindWindowByID(id);
4961 }
4962 
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)4963 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
4964 {
4965     window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
4966     if (settings->Size.x > 0 && settings->Size.y > 0)
4967         window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
4968     window->Collapsed = settings->Collapsed;
4969 }
4970 
CreateNewWindow(const char * name,ImGuiWindowFlags flags)4971 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
4972 {
4973     ImGuiContext& g = *GImGui;
4974     //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4975 
4976     // Create window the first time
4977     ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4978     window->Flags = flags;
4979     g.WindowsById.SetVoidPtr(window->ID, window);
4980 
4981     // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4982     window->Pos = ImVec2(60, 60);
4983 
4984     // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4985     if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4986         if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4987         {
4988             // Retrieve settings from .ini file
4989             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
4990             SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4991             ApplyWindowSettings(window, settings);
4992         }
4993     window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4994 
4995     if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4996     {
4997         window->AutoFitFramesX = window->AutoFitFramesY = 2;
4998         window->AutoFitOnlyGrows = false;
4999     }
5000     else
5001     {
5002         if (window->Size.x <= 0.0f)
5003             window->AutoFitFramesX = 2;
5004         if (window->Size.y <= 0.0f)
5005             window->AutoFitFramesY = 2;
5006         window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5007     }
5008 
5009     g.WindowsFocusOrder.push_back(window);
5010     if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5011         g.Windows.push_front(window); // Quite slow but rare and only once
5012     else
5013         g.Windows.push_back(window);
5014     return window;
5015 }
5016 
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)5017 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
5018 {
5019     ImGuiContext& g = *GImGui;
5020     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5021     {
5022         // Using -1,-1 on either X/Y axis to preserve the current size.
5023         ImRect cr = g.NextWindowData.SizeConstraintRect;
5024         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5025         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5026         if (g.NextWindowData.SizeCallback)
5027         {
5028             ImGuiSizeCallbackData data;
5029             data.UserData = g.NextWindowData.SizeCallbackUserData;
5030             data.Pos = window->Pos;
5031             data.CurrentSize = window->SizeFull;
5032             data.DesiredSize = new_size;
5033             g.NextWindowData.SizeCallback(&data);
5034             new_size = data.DesiredSize;
5035         }
5036         new_size.x = IM_FLOOR(new_size.x);
5037         new_size.y = IM_FLOOR(new_size.y);
5038     }
5039 
5040     // Minimum size
5041     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5042     {
5043         ImGuiWindow* window_for_height = window;
5044         new_size = ImMax(new_size, g.Style.WindowMinSize);
5045         new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5046     }
5047     return new_size;
5048 }
5049 
CalcWindowContentSizes(ImGuiWindow * window,ImVec2 * content_size_current,ImVec2 * content_size_ideal)5050 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
5051 {
5052     bool preserve_old_content_sizes = false;
5053     if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5054         preserve_old_content_sizes = true;
5055     else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5056         preserve_old_content_sizes = true;
5057     if (preserve_old_content_sizes)
5058     {
5059         *content_size_current = window->ContentSize;
5060         *content_size_ideal = window->ContentSizeIdeal;
5061         return;
5062     }
5063 
5064     content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5065     content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5066     content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
5067     content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
5068 }
5069 
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)5070 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5071 {
5072     ImGuiContext& g = *GImGui;
5073     ImGuiStyle& style = g.Style;
5074     ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
5075     ImVec2 size_pad = window->WindowPadding * 2.0f;
5076     ImVec2 size_desired = size_contents + size_pad + size_decorations;
5077     if (window->Flags & ImGuiWindowFlags_Tooltip)
5078     {
5079         // Tooltip always resize
5080         return size_desired;
5081     }
5082     else
5083     {
5084         // Maximum window size is determined by the viewport size or monitor size
5085         const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5086         const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5087         ImVec2 size_min = style.WindowMinSize;
5088         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)
5089             size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5090         ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
5091 
5092         // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5093         // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5094         ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5095         bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
5096         bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
5097         if (will_have_scrollbar_x)
5098             size_auto_fit.y += style.ScrollbarSize;
5099         if (will_have_scrollbar_y)
5100             size_auto_fit.x += style.ScrollbarSize;
5101         return size_auto_fit;
5102     }
5103 }
5104 
CalcWindowNextAutoFitSize(ImGuiWindow * window)5105 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
5106 {
5107     ImVec2 size_contents_current;
5108     ImVec2 size_contents_ideal;
5109     CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
5110     ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
5111     ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5112     return size_final;
5113 }
5114 
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5115 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5116 {
5117     if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5118         return ImGuiCol_PopupBg;
5119     if (flags & ImGuiWindowFlags_ChildWindow)
5120         return ImGuiCol_ChildBg;
5121     return ImGuiCol_WindowBg;
5122 }
5123 
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5124 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5125 {
5126     ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
5127     ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5128     ImVec2 size_expected = pos_max - pos_min;
5129     ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5130     *out_pos = pos_min;
5131     if (corner_norm.x == 0.0f)
5132         out_pos->x -= (size_constrained.x - size_expected.x);
5133     if (corner_norm.y == 0.0f)
5134         out_pos->y -= (size_constrained.y - size_expected.y);
5135     *out_size = size_constrained;
5136 }
5137 
5138 struct ImGuiResizeGripDef
5139 {
5140     ImVec2  CornerPosN;
5141     ImVec2  InnerDir;
5142     int     AngleMin12, AngleMax12;
5143 };
5144 
5145 static const ImGuiResizeGripDef resize_grip_def[4] =
5146 {
5147     { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
5148     { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
5149     { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
5150     { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused)
5151 };
5152 
5153 struct ImGuiResizeBorderDef
5154 {
5155     ImVec2 InnerDir;
5156     ImVec2 CornerPosN1, CornerPosN2;
5157     float  OuterAngle;
5158 };
5159 
5160 static const ImGuiResizeBorderDef resize_border_def[4] =
5161 {
5162     { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top
5163     { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5164     { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom
5165     { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left
5166 };
5167 
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5168 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5169 {
5170     ImRect rect = window->Rect();
5171     if (thickness == 0.0f) rect.Max -= ImVec2(1, 1);
5172     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
5173     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
5174     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
5175     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
5176     IM_ASSERT(0);
5177     return ImRect();
5178 }
5179 
5180 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
5181 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)5182 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
5183 {
5184     IM_ASSERT(n >= 0 && n <= 7);
5185     ImGuiID id = window->ID;
5186     id = ImHashStr("#RESIZE", 0, id);
5187     id = ImHashData(&n, sizeof(int), id);
5188     return id;
5189 }
5190 
5191 // Handle resize for: Resize Grips, Borders, Gamepad
5192 // Return true when using auto-fit (double click on resize grip)
UpdateWindowManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4],const ImRect & visibility_rect)5193 static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
5194 {
5195     ImGuiContext& g = *GImGui;
5196     ImGuiWindowFlags flags = window->Flags;
5197 
5198     if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5199         return false;
5200     if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5201         return false;
5202 
5203     bool ret_auto_fit = false;
5204     const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5205     const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5206     const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5207     const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
5208 
5209     ImVec2 pos_target(FLT_MAX, FLT_MAX);
5210     ImVec2 size_target(FLT_MAX, FLT_MAX);
5211 
5212     // Resize grips and borders are on layer 1
5213     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5214 
5215     // Manual resize grips
5216     PushID("#RESIZE");
5217     for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5218     {
5219         const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5220         const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5221 
5222         // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5223         ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
5224         if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5225         if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5226         bool hovered, held;
5227         ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5228         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5229         if (hovered || held)
5230             g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5231 
5232         if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5233         {
5234             // Manual auto-fit when double-clicking
5235             size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5236             ret_auto_fit = true;
5237             ClearActiveID();
5238         }
5239         else if (held)
5240         {
5241             // Resize from any of the four corners
5242             // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5243             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
5244             ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5245             ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5246             corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5247             CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
5248         }
5249         if (resize_grip_n == 0 || held || hovered)
5250             resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5251     }
5252     for (int border_n = 0; border_n < resize_border_count; border_n++)
5253     {
5254         bool hovered, held;
5255         ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
5256         ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5257         //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5258         if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5259         {
5260             g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5261             if (held)
5262                 *border_held = border_n;
5263         }
5264         if (held)
5265         {
5266             ImVec2 border_target = window->Pos;
5267             ImVec2 border_posn;
5268             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
5269             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
5270             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
5271             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
5272             ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX);
5273             ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX);
5274             border_target = ImClamp(border_target, clamp_min, clamp_max);
5275             CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5276         }
5277     }
5278     PopID();
5279 
5280     // Restore nav layer
5281     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5282 
5283     // Navigation resize (keyboard/gamepad)
5284     if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5285     {
5286         ImVec2 nav_resize_delta;
5287         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5288             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5289         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5290             nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5291         if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5292         {
5293             const float NAV_RESIZE_SPEED = 600.0f;
5294             nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5295             nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5296             g.NavWindowingToggleLayer = false;
5297             g.NavDisableMouseHover = true;
5298             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5299             // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5300             size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5301         }
5302     }
5303 
5304     // Apply back modified position/size to window
5305     if (size_target.x != FLT_MAX)
5306     {
5307         window->SizeFull = size_target;
5308         MarkIniSettingsDirty(window);
5309     }
5310     if (pos_target.x != FLT_MAX)
5311     {
5312         window->Pos = ImFloor(pos_target);
5313         MarkIniSettingsDirty(window);
5314     }
5315 
5316     window->Size = window->SizeFull;
5317     return ret_auto_fit;
5318 }
5319 
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5320 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5321 {
5322     ImGuiContext& g = *GImGui;
5323     ImVec2 size_for_clamping = window->Size;
5324     if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5325         size_for_clamping.y = window->TitleBarHeight();
5326     window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5327 }
5328 
RenderWindowOuterBorders(ImGuiWindow * window)5329 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5330 {
5331     ImGuiContext& g = *GImGui;
5332     float rounding = window->WindowRounding;
5333     float border_size = window->WindowBorderSize;
5334     if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5335         window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5336 
5337     int border_held = window->ResizeBorderHeld;
5338     if (border_held != -1)
5339     {
5340         const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5341         ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5342         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);
5343         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);
5344         window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5345     }
5346     if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5347     {
5348         float y = window->Pos.y + window->TitleBarHeight() - 1;
5349         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);
5350     }
5351 }
5352 
5353 // Draw background and borders
5354 // Draw and handle scrollbars
RenderWindowDecorations(ImGuiWindow * window,const ImRect & title_bar_rect,bool title_bar_is_highlight,int resize_grip_count,const ImU32 resize_grip_col[4],float resize_grip_draw_size)5355 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
5356 {
5357     ImGuiContext& g = *GImGui;
5358     ImGuiStyle& style = g.Style;
5359     ImGuiWindowFlags flags = window->Flags;
5360 
5361     // Ensure that ScrollBar doesn't read last frame's SkipItems
5362     IM_ASSERT(window->BeginCount == 0);
5363     window->SkipItems = false;
5364 
5365     // Draw window + handle manual resize
5366     // 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.
5367     const float window_rounding = window->WindowRounding;
5368     const float window_border_size = window->WindowBorderSize;
5369     if (window->Collapsed)
5370     {
5371         // Title bar only
5372         float backup_border_size = style.FrameBorderSize;
5373         g.Style.FrameBorderSize = window->WindowBorderSize;
5374         ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5375         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5376         g.Style.FrameBorderSize = backup_border_size;
5377     }
5378     else
5379     {
5380         // Window background
5381         if (!(flags & ImGuiWindowFlags_NoBackground))
5382         {
5383             ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5384             bool override_alpha = false;
5385             float alpha = 1.0f;
5386             if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5387             {
5388                 alpha = g.NextWindowData.BgAlphaVal;
5389                 override_alpha = true;
5390             }
5391             if (override_alpha)
5392                 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5393             window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5394         }
5395 
5396         // Title bar
5397         if (!(flags & ImGuiWindowFlags_NoTitleBar))
5398         {
5399             ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5400             window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5401         }
5402 
5403         // Menu bar
5404         if (flags & ImGuiWindowFlags_MenuBar)
5405         {
5406             ImRect menu_bar_rect = window->MenuBarRect();
5407             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.
5408             window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5409             if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5410                 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5411         }
5412 
5413         // Scrollbars
5414         if (window->ScrollbarX)
5415             Scrollbar(ImGuiAxis_X);
5416         if (window->ScrollbarY)
5417             Scrollbar(ImGuiAxis_Y);
5418 
5419         // Render resize grips (after their input handling so we don't have a frame of latency)
5420         if (!(flags & ImGuiWindowFlags_NoResize))
5421         {
5422             for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5423             {
5424                 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5425                 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5426                 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
5427                 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
5428                 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);
5429                 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5430             }
5431         }
5432 
5433         // Borders
5434         RenderWindowOuterBorders(window);
5435     }
5436 }
5437 
5438 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5439 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5440 {
5441     ImGuiContext& g = *GImGui;
5442     ImGuiStyle& style = g.Style;
5443     ImGuiWindowFlags flags = window->Flags;
5444 
5445     const bool has_close_button = (p_open != NULL);
5446     const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5447 
5448     // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5449     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5450     window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5451     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5452 
5453     // Layout buttons
5454     // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5455     float pad_l = style.FramePadding.x;
5456     float pad_r = style.FramePadding.x;
5457     float button_sz = g.FontSize;
5458     ImVec2 close_button_pos;
5459     ImVec2 collapse_button_pos;
5460     if (has_close_button)
5461     {
5462         pad_r += button_sz;
5463         close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5464     }
5465     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5466     {
5467         pad_r += button_sz;
5468         collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5469     }
5470     if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5471     {
5472         collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5473         pad_l += button_sz;
5474     }
5475 
5476     // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5477     if (has_collapse_button)
5478         if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5479             window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5480 
5481     // Close button
5482     if (has_close_button)
5483         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5484             *p_open = false;
5485 
5486     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5487     window->DC.ItemFlags = item_flags_backup;
5488 
5489     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5490     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5491     const char* UNSAVED_DOCUMENT_MARKER = "*";
5492     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5493     const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5494 
5495     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5496     // while uncentered title text will still reach edges correct.
5497     if (pad_l > style.FramePadding.x)
5498         pad_l += g.Style.ItemInnerSpacing.x;
5499     if (pad_r > style.FramePadding.x)
5500         pad_r += g.Style.ItemInnerSpacing.x;
5501     if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5502     {
5503         float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5504         float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5505         pad_l = ImMax(pad_l, pad_extend * centerness);
5506         pad_r = ImMax(pad_r, pad_extend * centerness);
5507     }
5508 
5509     ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);
5510     ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5511     //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5512     RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5513     if (flags & ImGuiWindowFlags_UnsavedDocument)
5514     {
5515         ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5516         ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5517         RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5518     }
5519 }
5520 
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5521 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5522 {
5523     window->ParentWindow = parent_window;
5524     window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5525     if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5526         window->RootWindow = parent_window->RootWindow;
5527     if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5528         window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5529     while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5530     {
5531         IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5532         window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5533     }
5534 }
5535 
5536 // Push a new Dear ImGui window to add widgets to.
5537 // - 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.
5538 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5539 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5540 //   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.
5541 // - 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.
5542 // - 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)5543 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5544 {
5545     ImGuiContext& g = *GImGui;
5546     const ImGuiStyle& style = g.Style;
5547     IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
5548     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
5549     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5550 
5551     // Find or create
5552     ImGuiWindow* window = FindWindowByName(name);
5553     const bool window_just_created = (window == NULL);
5554     if (window_just_created)
5555         window = CreateNewWindow(name, flags);
5556 
5557     // Automatically disable manual moving/resizing when NoInputs is set
5558     if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5559         flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5560 
5561     if (flags & ImGuiWindowFlags_NavFlattened)
5562         IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5563 
5564     const int current_frame = g.FrameCount;
5565     const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5566     window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5567 
5568     // Update the Appearing flag
5569     bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5570     const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5571     if (flags & ImGuiWindowFlags_Popup)
5572     {
5573         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5574         window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5575         window_just_activated_by_user |= (window != popup_ref.Window);
5576     }
5577     window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5578     if (window->Appearing)
5579         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5580 
5581     // Update Flags, LastFrameActive, BeginOrderXXX fields
5582     if (first_begin_of_the_frame)
5583     {
5584         window->Flags = (ImGuiWindowFlags)flags;
5585         window->LastFrameActive = current_frame;
5586         window->LastTimeActive = (float)g.Time;
5587         window->BeginOrderWithinParent = 0;
5588         window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5589     }
5590     else
5591     {
5592         flags = window->Flags;
5593     }
5594 
5595     // 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
5596     ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5597     ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5598     IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5599 
5600     // We allow window memory to be compacted so recreate the base stack when needed.
5601     if (window->IDStack.Size == 0)
5602         window->IDStack.push_back(window->ID);
5603 
5604     // Add to stack
5605     // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5606     g.CurrentWindowStack.push_back(window);
5607     g.CurrentWindow = window;
5608     window->DC.StackSizesOnBegin.SetToCurrentState();
5609     g.CurrentWindow = NULL;
5610 
5611     if (flags & ImGuiWindowFlags_Popup)
5612     {
5613         ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5614         popup_ref.Window = window;
5615         g.BeginPopupStack.push_back(popup_ref);
5616         window->PopupId = popup_ref.PopupId;
5617     }
5618 
5619     if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5620         window->NavLastIds[0] = 0;
5621 
5622     // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5623     if (first_begin_of_the_frame)
5624         UpdateWindowParentAndRootLinks(window, flags, parent_window);
5625 
5626     // Process SetNextWindow***() calls
5627     // (FIXME: Consider splitting the HasXXX flags into X/Y components
5628     bool window_pos_set_by_api = false;
5629     bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5630     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5631     {
5632         window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5633         if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5634         {
5635             // May be processed on the next frame if this is our first frame and we are measuring size
5636             // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5637             window->SetWindowPosVal = g.NextWindowData.PosVal;
5638             window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5639             window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5640         }
5641         else
5642         {
5643             SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5644         }
5645     }
5646     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5647     {
5648         window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5649         window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5650         SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5651     }
5652     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
5653     {
5654         if (g.NextWindowData.ScrollVal.x >= 0.0f)
5655         {
5656             window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
5657             window->ScrollTargetCenterRatio.x = 0.0f;
5658         }
5659         if (g.NextWindowData.ScrollVal.y >= 0.0f)
5660         {
5661             window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
5662             window->ScrollTargetCenterRatio.y = 0.0f;
5663         }
5664     }
5665     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5666         window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5667     else if (first_begin_of_the_frame)
5668         window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5669     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5670         SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5671     if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5672         FocusWindow(window);
5673     if (window->Appearing)
5674         SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5675 
5676     // When reusing window again multiple times a frame, just append content (don't need to setup again)
5677     if (first_begin_of_the_frame)
5678     {
5679         // Initialize
5680         const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5681         window->Active = true;
5682         window->HasCloseButton = (p_open != NULL);
5683         window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5684         window->IDStack.resize(1);
5685         window->DrawList->_ResetForNewFrame();
5686         window->DC.CurrentTableIdx = -1;
5687 
5688         // Restore buffer capacity when woken from a compacted state, to avoid
5689         if (window->MemoryCompacted)
5690             GcAwakeTransientWindowBuffers(window);
5691 
5692         // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5693         // 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.
5694         bool window_title_visible_elsewhere = false;
5695         if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
5696             window_title_visible_elsewhere = true;
5697         if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5698         {
5699             size_t buf_len = (size_t)window->NameBufLen;
5700             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5701             window->NameBufLen = (int)buf_len;
5702         }
5703 
5704         // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5705 
5706         // Update contents size from last frame for auto-fitting (or use explicit size)
5707         CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
5708         if (window->HiddenFramesCanSkipItems > 0)
5709             window->HiddenFramesCanSkipItems--;
5710         if (window->HiddenFramesCannotSkipItems > 0)
5711             window->HiddenFramesCannotSkipItems--;
5712         if (window->HiddenFramesForRenderOnly > 0)
5713             window->HiddenFramesForRenderOnly--;
5714 
5715         // Hide new windows for one frame until they calculate their size
5716         if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5717             window->HiddenFramesCannotSkipItems = 1;
5718 
5719         // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5720         // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5721         if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5722         {
5723             window->HiddenFramesCannotSkipItems = 1;
5724             if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5725             {
5726                 if (!window_size_x_set_by_api)
5727                     window->Size.x = window->SizeFull.x = 0.f;
5728                 if (!window_size_y_set_by_api)
5729                     window->Size.y = window->SizeFull.y = 0.f;
5730                 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
5731             }
5732         }
5733 
5734         // SELECT VIEWPORT
5735         // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5736         SetCurrentWindow(window);
5737 
5738         // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5739 
5740         if (flags & ImGuiWindowFlags_ChildWindow)
5741             window->WindowBorderSize = style.ChildBorderSize;
5742         else
5743             window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5744         window->WindowPadding = style.WindowPadding;
5745         if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5746             window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5747 
5748         // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
5749         window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5750         window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5751 
5752         // Collapse window by double-clicking on title bar
5753         // 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
5754         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5755         {
5756             // 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.
5757             ImRect title_bar_rect = window->TitleBarRect();
5758             if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5759                 window->WantCollapseToggle = true;
5760             if (window->WantCollapseToggle)
5761             {
5762                 window->Collapsed = !window->Collapsed;
5763                 MarkIniSettingsDirty(window);
5764                 FocusWindow(window);
5765             }
5766         }
5767         else
5768         {
5769             window->Collapsed = false;
5770         }
5771         window->WantCollapseToggle = false;
5772 
5773         // SIZE
5774 
5775         // Calculate auto-fit size, handle automatic resize
5776         const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
5777         bool use_current_size_for_scrollbar_x = window_just_created;
5778         bool use_current_size_for_scrollbar_y = window_just_created;
5779         if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5780         {
5781             // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5782             if (!window_size_x_set_by_api)
5783             {
5784                 window->SizeFull.x = size_auto_fit.x;
5785                 use_current_size_for_scrollbar_x = true;
5786             }
5787             if (!window_size_y_set_by_api)
5788             {
5789                 window->SizeFull.y = size_auto_fit.y;
5790                 use_current_size_for_scrollbar_y = true;
5791             }
5792         }
5793         else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5794         {
5795             // Auto-fit may only grow window during the first few frames
5796             // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5797             if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5798             {
5799                 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5800                 use_current_size_for_scrollbar_x = true;
5801             }
5802             if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5803             {
5804                 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5805                 use_current_size_for_scrollbar_y = true;
5806             }
5807             if (!window->Collapsed)
5808                 MarkIniSettingsDirty(window);
5809         }
5810 
5811         // Apply minimum/maximum window size constraints and final size
5812         window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5813         window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5814 
5815         // Decoration size
5816         const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5817 
5818         // POSITION
5819 
5820         // Popup latch its initial position, will position itself when it appears next frame
5821         if (window_just_activated_by_user)
5822         {
5823             window->AutoPosLastDirection = ImGuiDir_None;
5824             if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
5825                 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5826         }
5827 
5828         // Position child window
5829         if (flags & ImGuiWindowFlags_ChildWindow)
5830         {
5831             IM_ASSERT(parent_window && parent_window->Active);
5832             window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5833             parent_window->DC.ChildWindows.push_back(window);
5834             if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5835                 window->Pos = parent_window->DC.CursorPos;
5836         }
5837 
5838         const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5839         if (window_pos_with_pivot)
5840             SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5841         else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5842             window->Pos = FindBestWindowPosForPopup(window);
5843         else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5844             window->Pos = FindBestWindowPosForPopup(window);
5845         else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5846             window->Pos = FindBestWindowPosForPopup(window);
5847 
5848         // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
5849         // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
5850         ImRect viewport_rect(GetViewportRect());
5851         ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5852         ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding);
5853 
5854         // Clamp position/size so window stays visible within its viewport or monitor
5855         // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5856         if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5857             if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
5858                 ClampWindowRect(window, visibility_rect);
5859         window->Pos = ImFloor(window->Pos);
5860 
5861         // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5862         // Large values tend to lead to variety of artifacts and are not recommended.
5863         window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5864 
5865         // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
5866         //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5867         //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
5868 
5869         // Apply window focus (new and reactivated windows are moved to front)
5870         bool want_focus = false;
5871         if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5872         {
5873             if (flags & ImGuiWindowFlags_Popup)
5874                 want_focus = true;
5875             else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5876                 want_focus = true;
5877         }
5878 
5879         // Handle manual resize: Resize Grips, Borders, Gamepad
5880         int border_held = -1;
5881         ImU32 resize_grip_col[4] = {};
5882         const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
5883         const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5884         if (!window->Collapsed)
5885             if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
5886                 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5887         window->ResizeBorderHeld = (signed char)border_held;
5888 
5889         // SCROLLBAR VISIBILITY
5890 
5891         // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5892         if (!window->Collapsed)
5893         {
5894             // When reading the current size we need to read it after size constraints have been applied.
5895             // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5896             ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5897             ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5898             ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5899             float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5900             float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5901             //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5902             window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5903             window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
5904             if (window->ScrollbarX && !window->ScrollbarY)
5905                 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5906             window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5907         }
5908 
5909         // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5910         // Update various regions. Variables they depends on should be set above in this function.
5911         // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5912 
5913         // Outer rectangle
5914         // Not affected by window border size. Used by:
5915         // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5916         // - Begin() initial clipping rect for drawing window background and borders.
5917         // - Begin() clipping whole child
5918         const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5919         const ImRect outer_rect = window->Rect();
5920         const ImRect title_bar_rect = window->TitleBarRect();
5921         window->OuterRectClipped = outer_rect;
5922         window->OuterRectClipped.ClipWith(host_rect);
5923 
5924         // Inner rectangle
5925         // Not affected by window border size. Used by:
5926         // - InnerClipRect
5927         // - ScrollToBringRectIntoView()
5928         // - NavUpdatePageUpPageDown()
5929         // - Scrollbar()
5930         window->InnerRect.Min.x = window->Pos.x;
5931         window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5932         window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5933         window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5934 
5935         // Inner clipping rectangle.
5936         // Will extend a little bit outside the normal work region.
5937         // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5938         // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5939         // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5940         // Affected by window/frame border size. Used by:
5941         // - Begin() initial clip rect
5942         float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5943         window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5944         window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5945         window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5946         window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5947         window->InnerClipRect.ClipWithFull(host_rect);
5948 
5949         // Default item width. Make it proportional to window size if window manually resizes
5950         if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5951             window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5952         else
5953             window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5954 
5955         // SCROLLING
5956 
5957         // Lock down maximum scrolling
5958         // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5959         // for right/bottom aligned items without creating a scrollbar.
5960         window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5961         window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5962 
5963         // Apply scrolling
5964         window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
5965         window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5966 
5967         // DRAWING
5968 
5969         // Setup draw list and outer clipping rectangle
5970         IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
5971         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5972         PushClipRect(host_rect.Min, host_rect.Max, false);
5973 
5974         // Draw modal window background (darkens what is behind them, all viewports)
5975         const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5976         const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5977         if (dim_bg_for_modal || dim_bg_for_window_list)
5978         {
5979             const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5980             window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5981         }
5982 
5983         // Draw navigation selection/windowing rectangle background
5984         if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5985         {
5986             ImRect bb = window->Rect();
5987             bb.Expand(g.FontSize);
5988             if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5989                 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5990         }
5991 
5992         // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5993         // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5994         // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5995         // We also disabled this when we have dimming overlay behind this specific one child.
5996         // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected.
5997         {
5998             bool render_decorations_in_parent = false;
5999             if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6000                 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
6001                     render_decorations_in_parent = true;
6002             if (render_decorations_in_parent)
6003                 window->DrawList = parent_window->DrawList;
6004 
6005             // Handle title bar, scrollbar, resize grips and resize borders
6006             const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6007             const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
6008             RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6009 
6010             if (render_decorations_in_parent)
6011                 window->DrawList = &window->DrawListInst;
6012         }
6013 
6014         // Draw navigation selection/windowing rectangle border
6015         if (g.NavWindowingTargetAnim == window)
6016         {
6017             float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6018             ImRect bb = window->Rect();
6019             bb.Expand(g.FontSize);
6020             if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6021             {
6022                 bb.Expand(-g.FontSize - 1.0f);
6023                 rounding = window->WindowRounding;
6024             }
6025             window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
6026         }
6027 
6028         // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6029 
6030         // Work rectangle.
6031         // Affected by window padding and border size. Used by:
6032         // - Columns() for right-most edge
6033         // - TreeNode(), CollapsingHeader() for right-most edge
6034         // - BeginTabBar() for right-most edge
6035         const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6036         const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6037         const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x));
6038         const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y));
6039         window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6040         window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6041         window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6042         window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6043         window->ParentWorkRect = window->WorkRect;
6044 
6045         // [LEGACY] Content Region
6046         // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6047         // Used by:
6048         // - Mouse wheel scrolling + many other things
6049         window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6050         window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6051         window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x));
6052         window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y));
6053 
6054         // Setup drawing context
6055         // (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.)
6056         window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6057         window->DC.GroupOffset.x = 0.0f;
6058         window->DC.ColumnsOffset.x = 0.0f;
6059         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6060         window->DC.CursorPos = window->DC.CursorStartPos;
6061         window->DC.CursorPosPrevLine = window->DC.CursorPos;
6062         window->DC.CursorMaxPos = window->DC.CursorStartPos;
6063         window->DC.IdealMaxPos = window->DC.CursorStartPos;
6064         window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6065         window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6066 
6067         window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6068         window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
6069         window->DC.NavLayerActiveMaskNext = 0x00;
6070         window->DC.NavHideHighlightOneFrame = false;
6071         window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6072 
6073         window->DC.MenuBarAppending = false;
6074         window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
6075         window->DC.TreeDepth = 0;
6076         window->DC.TreeJumpToParentOnPopMask = 0x00;
6077         window->DC.ChildWindows.resize(0);
6078         window->DC.StateStorage = &window->StateStorage;
6079         window->DC.CurrentColumns = NULL;
6080         window->DC.LayoutType = ImGuiLayoutType_Vertical;
6081         window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6082         window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6083 
6084         window->DC.ItemWidth = window->ItemWidthDefault;
6085         window->DC.TextWrapPos = -1.0f; // disabled
6086         window->DC.ItemWidthStack.resize(0);
6087         window->DC.TextWrapPosStack.resize(0);
6088 
6089         if (window->AutoFitFramesX > 0)
6090             window->AutoFitFramesX--;
6091         if (window->AutoFitFramesY > 0)
6092             window->AutoFitFramesY--;
6093 
6094         // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6095         if (want_focus)
6096         {
6097             FocusWindow(window);
6098             NavInitWindow(window, false);
6099         }
6100 
6101         // Title bar
6102         if (!(flags & ImGuiWindowFlags_NoTitleBar))
6103             RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
6104 
6105         // Clear hit test shape every frame
6106         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6107 
6108         // Pressing CTRL+C while holding on a window copy its content to the clipboard
6109         // 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.
6110         // Maybe we can support CTRL+C on every element?
6111         /*
6112         //if (g.NavWindow == window && g.ActiveId == 0)
6113         if (g.ActiveId == window->MoveId)
6114             if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6115                 LogToClipboard();
6116         */
6117 
6118         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6119         // This is useful to allow creating context menus on title bar only, etc.
6120         SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
6121 
6122 #ifdef IMGUI_ENABLE_TEST_ENGINE
6123         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6124             IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
6125 #endif
6126     }
6127     else
6128     {
6129         // Append
6130         SetCurrentWindow(window);
6131     }
6132 
6133     // Pull/inherit current state
6134     window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack
6135     window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595
6136 
6137     PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6138 
6139     // 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)
6140     window->WriteAccessed = false;
6141     window->BeginCount++;
6142     g.NextWindowData.ClearFlags();
6143 
6144     // Update visibility
6145     if (first_begin_of_the_frame)
6146     {
6147         if (flags & ImGuiWindowFlags_ChildWindow)
6148         {
6149             // Child window can be out of sight and have "negative" clip windows.
6150             // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6151             IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6152             if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
6153                 if (!g.LogEnabled)
6154                     if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6155                         window->HiddenFramesCanSkipItems = 1;
6156 
6157             // Hide along with parent or if parent is collapsed
6158             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6159                 window->HiddenFramesCanSkipItems = 1;
6160             if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6161                 window->HiddenFramesCannotSkipItems = 1;
6162         }
6163 
6164         // 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)
6165         if (style.Alpha <= 0.0f)
6166             window->HiddenFramesCanSkipItems = 1;
6167 
6168         // Update the Hidden flag
6169         window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0);
6170 
6171         // Update the SkipItems flag, used to early out of all items functions (no layout required)
6172         bool skip_items = false;
6173         if (window->Collapsed || !window->Active || window->Hidden)
6174             if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6175                 skip_items = true;
6176         window->SkipItems = skip_items;
6177     }
6178 
6179     return !window->SkipItems;
6180 }
6181 
End()6182 void ImGui::End()
6183 {
6184     ImGuiContext& g = *GImGui;
6185     ImGuiWindow* window = g.CurrentWindow;
6186 
6187     // Error checking: verify that user hasn't called End() too many times!
6188     if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6189     {
6190         IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6191         return;
6192     }
6193     IM_ASSERT(g.CurrentWindowStack.Size > 0);
6194 
6195     // Error checking: verify that user doesn't directly call End() on a child window.
6196     if (window->Flags & ImGuiWindowFlags_ChildWindow)
6197         IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6198 
6199     // Close anything that is open
6200     if (window->DC.CurrentColumns)
6201         EndColumns();
6202     PopClipRect();   // Inner window clip rectangle
6203 
6204     // Stop logging
6205     if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
6206         LogFinish();
6207 
6208     // Pop from window stack
6209     g.CurrentWindowStack.pop_back();
6210     if (window->Flags & ImGuiWindowFlags_Popup)
6211         g.BeginPopupStack.pop_back();
6212     window->DC.StackSizesOnBegin.CompareWithCurrentState();
6213     SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6214 }
6215 
BringWindowToFocusFront(ImGuiWindow * window)6216 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6217 {
6218     ImGuiContext& g = *GImGui;
6219     if (g.WindowsFocusOrder.back() == window)
6220         return;
6221     for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
6222         if (g.WindowsFocusOrder[i] == window)
6223         {
6224             memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
6225             g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
6226             break;
6227         }
6228 }
6229 
BringWindowToDisplayFront(ImGuiWindow * window)6230 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6231 {
6232     ImGuiContext& g = *GImGui;
6233     ImGuiWindow* current_front_window = g.Windows.back();
6234     if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6235         return;
6236     for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6237         if (g.Windows[i] == window)
6238         {
6239             memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6240             g.Windows[g.Windows.Size - 1] = window;
6241             break;
6242         }
6243 }
6244 
BringWindowToDisplayBack(ImGuiWindow * window)6245 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6246 {
6247     ImGuiContext& g = *GImGui;
6248     if (g.Windows[0] == window)
6249         return;
6250     for (int i = 0; i < g.Windows.Size; i++)
6251         if (g.Windows[i] == window)
6252         {
6253             memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6254             g.Windows[0] = window;
6255             break;
6256         }
6257 }
6258 
6259 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6260 void ImGui::FocusWindow(ImGuiWindow* window)
6261 {
6262     ImGuiContext& g = *GImGui;
6263 
6264     if (g.NavWindow != window)
6265     {
6266         g.NavWindow = window;
6267         if (window && g.NavDisableMouseHover)
6268             g.NavMousePosDirty = true;
6269         g.NavInitRequest = false;
6270         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6271         g.NavFocusScopeId = 0;
6272         g.NavIdIsAlive = false;
6273         g.NavLayer = ImGuiNavLayer_Main;
6274         //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6275     }
6276 
6277     // Close popups if any
6278     ClosePopupsOverWindow(window, false);
6279 
6280     // Move the root window to the top of the pile
6281     IM_ASSERT(window == NULL || window->RootWindow != NULL);
6282     ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
6283     ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6284 
6285     // Steal active widgets. Some of the cases it triggers includes:
6286     // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6287     // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6288     if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6289         if (!g.ActiveIdNoClearOnFocusLoss)
6290             ClearActiveID();
6291 
6292     // Passing NULL allow to disable keyboard focus
6293     if (!window)
6294         return;
6295 
6296     // Bring to front
6297     BringWindowToFocusFront(focus_front_window);
6298     if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6299         BringWindowToDisplayFront(display_front_window);
6300 }
6301 
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6302 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6303 {
6304     ImGuiContext& g = *GImGui;
6305 
6306     int start_idx = g.WindowsFocusOrder.Size - 1;
6307     if (under_this_window != NULL)
6308     {
6309         int under_this_window_idx = FindWindowFocusIndex(under_this_window);
6310         if (under_this_window_idx != -1)
6311             start_idx = under_this_window_idx - 1;
6312     }
6313     for (int i = start_idx; i >= 0; i--)
6314     {
6315         // 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.
6316         ImGuiWindow* window = g.WindowsFocusOrder[i];
6317         if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
6318             if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6319             {
6320                 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6321                 FocusWindow(focus_window);
6322                 return;
6323             }
6324     }
6325     FocusWindow(NULL);
6326 }
6327 
SetCurrentFont(ImFont * font)6328 void ImGui::SetCurrentFont(ImFont* font)
6329 {
6330     ImGuiContext& g = *GImGui;
6331     IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6332     IM_ASSERT(font->Scale > 0.0f);
6333     g.Font = font;
6334     g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6335     g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6336 
6337     ImFontAtlas* atlas = g.Font->ContainerAtlas;
6338     g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6339     g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6340     g.DrawListSharedData.Font = g.Font;
6341     g.DrawListSharedData.FontSize = g.FontSize;
6342 }
6343 
PushFont(ImFont * font)6344 void ImGui::PushFont(ImFont* font)
6345 {
6346     ImGuiContext& g = *GImGui;
6347     if (!font)
6348         font = GetDefaultFont();
6349     SetCurrentFont(font);
6350     g.FontStack.push_back(font);
6351     g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6352 }
6353 
PopFont()6354 void  ImGui::PopFont()
6355 {
6356     ImGuiContext& g = *GImGui;
6357     g.CurrentWindow->DrawList->PopTextureID();
6358     g.FontStack.pop_back();
6359     SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6360 }
6361 
PushItemFlag(ImGuiItemFlags option,bool enabled)6362 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6363 {
6364     ImGuiContext& g = *GImGui;
6365     ImGuiWindow* window = g.CurrentWindow;
6366     ImGuiItemFlags item_flags = window->DC.ItemFlags;
6367     IM_ASSERT(item_flags == g.ItemFlagsStack.back());
6368     if (enabled)
6369         item_flags |= option;
6370     else
6371         item_flags &= ~option;
6372     window->DC.ItemFlags = item_flags;
6373     g.ItemFlagsStack.push_back(item_flags);
6374 }
6375 
PopItemFlag()6376 void ImGui::PopItemFlag()
6377 {
6378     ImGuiContext& g = *GImGui;
6379     ImGuiWindow* window = g.CurrentWindow;
6380     IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
6381     g.ItemFlagsStack.pop_back();
6382     window->DC.ItemFlags = g.ItemFlagsStack.back();
6383 }
6384 
6385 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6386 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6387 {
6388     PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6389 }
6390 
PopAllowKeyboardFocus()6391 void ImGui::PopAllowKeyboardFocus()
6392 {
6393     PopItemFlag();
6394 }
6395 
PushButtonRepeat(bool repeat)6396 void ImGui::PushButtonRepeat(bool repeat)
6397 {
6398     PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6399 }
6400 
PopButtonRepeat()6401 void ImGui::PopButtonRepeat()
6402 {
6403     PopItemFlag();
6404 }
6405 
PushTextWrapPos(float wrap_pos_x)6406 void ImGui::PushTextWrapPos(float wrap_pos_x)
6407 {
6408     ImGuiWindow* window = GetCurrentWindow();
6409     window->DC.TextWrapPos = wrap_pos_x;
6410     window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6411 }
6412 
PopTextWrapPos()6413 void ImGui::PopTextWrapPos()
6414 {
6415     ImGuiWindow* window = GetCurrentWindow();
6416     window->DC.TextWrapPosStack.pop_back();
6417     window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6418 }
6419 
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6420 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6421 {
6422     if (window->RootWindow == potential_parent)
6423         return true;
6424     while (window != NULL)
6425     {
6426         if (window == potential_parent)
6427             return true;
6428         window = window->ParentWindow;
6429     }
6430     return false;
6431 }
6432 
IsWindowAbove(ImGuiWindow * potential_above,ImGuiWindow * potential_below)6433 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
6434 {
6435     ImGuiContext& g = *GImGui;
6436     for (int i = g.Windows.Size - 1; i >= 0; i--)
6437     {
6438         ImGuiWindow* candidate_window = g.Windows[i];
6439         if (candidate_window == potential_above)
6440             return true;
6441         if (candidate_window == potential_below)
6442             return false;
6443     }
6444     return false;
6445 }
6446 
IsWindowHovered(ImGuiHoveredFlags flags)6447 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6448 {
6449     IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0);   // Flags not supported by this function
6450     ImGuiContext& g = *GImGui;
6451 
6452     if (flags & ImGuiHoveredFlags_AnyWindow)
6453     {
6454         if (g.HoveredWindow == NULL)
6455             return false;
6456     }
6457     else
6458     {
6459         switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6460         {
6461         case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6462             if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6463                 return false;
6464             break;
6465         case ImGuiHoveredFlags_RootWindow:
6466             if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6467                 return false;
6468             break;
6469         case ImGuiHoveredFlags_ChildWindows:
6470             if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6471                 return false;
6472             break;
6473         default:
6474             if (g.HoveredWindow != g.CurrentWindow)
6475                 return false;
6476             break;
6477         }
6478     }
6479 
6480     if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6481         return false;
6482     if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6483         if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6484             return false;
6485     return true;
6486 }
6487 
IsWindowFocused(ImGuiFocusedFlags flags)6488 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6489 {
6490     ImGuiContext& g = *GImGui;
6491 
6492     if (flags & ImGuiFocusedFlags_AnyWindow)
6493         return g.NavWindow != NULL;
6494 
6495     IM_ASSERT(g.CurrentWindow);     // Not inside a Begin()/End()
6496     switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6497     {
6498     case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6499         return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6500     case ImGuiFocusedFlags_RootWindow:
6501         return g.NavWindow == g.CurrentWindow->RootWindow;
6502     case ImGuiFocusedFlags_ChildWindows:
6503         return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6504     default:
6505         return g.NavWindow == g.CurrentWindow;
6506     }
6507 }
6508 
6509 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6510 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
6511 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6512 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6513 {
6514     return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6515 }
6516 
GetWindowWidth()6517 float ImGui::GetWindowWidth()
6518 {
6519     ImGuiWindow* window = GImGui->CurrentWindow;
6520     return window->Size.x;
6521 }
6522 
GetWindowHeight()6523 float ImGui::GetWindowHeight()
6524 {
6525     ImGuiWindow* window = GImGui->CurrentWindow;
6526     return window->Size.y;
6527 }
6528 
GetWindowPos()6529 ImVec2 ImGui::GetWindowPos()
6530 {
6531     ImGuiContext& g = *GImGui;
6532     ImGuiWindow* window = g.CurrentWindow;
6533     return window->Pos;
6534 }
6535 
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6536 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6537 {
6538     // Test condition (NB: bit 0 is always true) and clear flags for next time
6539     if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6540         return;
6541 
6542     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6543     window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6544     window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6545 
6546     // Set
6547     const ImVec2 old_pos = window->Pos;
6548     window->Pos = ImFloor(pos);
6549     ImVec2 offset = window->Pos - old_pos;
6550     window->DC.CursorPos += offset;         // 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
6551     window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6552     window->DC.CursorStartPos += offset;
6553 }
6554 
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6555 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6556 {
6557     ImGuiWindow* window = GetCurrentWindowRead();
6558     SetWindowPos(window, pos, cond);
6559 }
6560 
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6561 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6562 {
6563     if (ImGuiWindow* window = FindWindowByName(name))
6564         SetWindowPos(window, pos, cond);
6565 }
6566 
GetWindowSize()6567 ImVec2 ImGui::GetWindowSize()
6568 {
6569     ImGuiWindow* window = GetCurrentWindowRead();
6570     return window->Size;
6571 }
6572 
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6573 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6574 {
6575     // Test condition (NB: bit 0 is always true) and clear flags for next time
6576     if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6577         return;
6578 
6579     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6580     window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6581 
6582     // Set
6583     if (size.x > 0.0f)
6584     {
6585         window->AutoFitFramesX = 0;
6586         window->SizeFull.x = IM_FLOOR(size.x);
6587     }
6588     else
6589     {
6590         window->AutoFitFramesX = 2;
6591         window->AutoFitOnlyGrows = false;
6592     }
6593     if (size.y > 0.0f)
6594     {
6595         window->AutoFitFramesY = 0;
6596         window->SizeFull.y = IM_FLOOR(size.y);
6597     }
6598     else
6599     {
6600         window->AutoFitFramesY = 2;
6601         window->AutoFitOnlyGrows = false;
6602     }
6603 }
6604 
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6605 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6606 {
6607     SetWindowSize(GImGui->CurrentWindow, size, cond);
6608 }
6609 
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6610 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6611 {
6612     if (ImGuiWindow* window = FindWindowByName(name))
6613         SetWindowSize(window, size, cond);
6614 }
6615 
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6616 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6617 {
6618     // Test condition (NB: bit 0 is always true) and clear flags for next time
6619     if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6620         return;
6621     window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6622 
6623     // Set
6624     window->Collapsed = collapsed;
6625 }
6626 
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)6627 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
6628 {
6629     IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
6630     window->HitTestHoleSize = ImVec2ih(size);
6631     window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
6632 }
6633 
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6634 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6635 {
6636     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6637 }
6638 
IsWindowCollapsed()6639 bool ImGui::IsWindowCollapsed()
6640 {
6641     ImGuiWindow* window = GetCurrentWindowRead();
6642     return window->Collapsed;
6643 }
6644 
IsWindowAppearing()6645 bool ImGui::IsWindowAppearing()
6646 {
6647     ImGuiWindow* window = GetCurrentWindowRead();
6648     return window->Appearing;
6649 }
6650 
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6651 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6652 {
6653     if (ImGuiWindow* window = FindWindowByName(name))
6654         SetWindowCollapsed(window, collapsed, cond);
6655 }
6656 
SetWindowFocus()6657 void ImGui::SetWindowFocus()
6658 {
6659     FocusWindow(GImGui->CurrentWindow);
6660 }
6661 
SetWindowFocus(const char * name)6662 void ImGui::SetWindowFocus(const char* name)
6663 {
6664     if (name)
6665     {
6666         if (ImGuiWindow* window = FindWindowByName(name))
6667             FocusWindow(window);
6668     }
6669     else
6670     {
6671         FocusWindow(NULL);
6672     }
6673 }
6674 
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6675 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6676 {
6677     ImGuiContext& g = *GImGui;
6678     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6679     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6680     g.NextWindowData.PosVal = pos;
6681     g.NextWindowData.PosPivotVal = pivot;
6682     g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6683 }
6684 
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6685 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6686 {
6687     ImGuiContext& g = *GImGui;
6688     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6689     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6690     g.NextWindowData.SizeVal = size;
6691     g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6692 }
6693 
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6694 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6695 {
6696     ImGuiContext& g = *GImGui;
6697     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6698     g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6699     g.NextWindowData.SizeCallback = custom_callback;
6700     g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6701 }
6702 
6703 // Content size = inner scrollable rectangle, padded with WindowPadding.
6704 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6705 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6706 {
6707     ImGuiContext& g = *GImGui;
6708     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6709     g.NextWindowData.ContentSizeVal = ImFloor(size);
6710 }
6711 
SetNextWindowScroll(const ImVec2 & scroll)6712 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
6713 {
6714     ImGuiContext& g = *GImGui;
6715     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
6716     g.NextWindowData.ScrollVal = scroll;
6717 }
6718 
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6719 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6720 {
6721     ImGuiContext& g = *GImGui;
6722     IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6723     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6724     g.NextWindowData.CollapsedVal = collapsed;
6725     g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6726 }
6727 
SetNextWindowFocus()6728 void ImGui::SetNextWindowFocus()
6729 {
6730     ImGuiContext& g = *GImGui;
6731     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6732 }
6733 
SetNextWindowBgAlpha(float alpha)6734 void ImGui::SetNextWindowBgAlpha(float alpha)
6735 {
6736     ImGuiContext& g = *GImGui;
6737     g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6738     g.NextWindowData.BgAlphaVal = alpha;
6739 }
6740 
GetWindowDrawList()6741 ImDrawList* ImGui::GetWindowDrawList()
6742 {
6743     ImGuiWindow* window = GetCurrentWindow();
6744     return window->DrawList;
6745 }
6746 
GetFont()6747 ImFont* ImGui::GetFont()
6748 {
6749     return GImGui->Font;
6750 }
6751 
GetFontSize()6752 float ImGui::GetFontSize()
6753 {
6754     return GImGui->FontSize;
6755 }
6756 
GetFontTexUvWhitePixel()6757 ImVec2 ImGui::GetFontTexUvWhitePixel()
6758 {
6759     return GImGui->DrawListSharedData.TexUvWhitePixel;
6760 }
6761 
SetWindowFontScale(float scale)6762 void ImGui::SetWindowFontScale(float scale)
6763 {
6764     IM_ASSERT(scale > 0.0f);
6765     ImGuiContext& g = *GImGui;
6766     ImGuiWindow* window = GetCurrentWindow();
6767     window->FontWindowScale = scale;
6768     g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6769 }
6770 
ActivateItem(ImGuiID id)6771 void ImGui::ActivateItem(ImGuiID id)
6772 {
6773     ImGuiContext& g = *GImGui;
6774     g.NavNextActivateId = id;
6775 }
6776 
PushFocusScope(ImGuiID id)6777 void ImGui::PushFocusScope(ImGuiID id)
6778 {
6779     ImGuiContext& g = *GImGui;
6780     ImGuiWindow* window = g.CurrentWindow;
6781     g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent);
6782     window->DC.NavFocusScopeIdCurrent = id;
6783 }
6784 
PopFocusScope()6785 void ImGui::PopFocusScope()
6786 {
6787     ImGuiContext& g = *GImGui;
6788     ImGuiWindow* window = g.CurrentWindow;
6789     IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
6790     window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back();
6791     g.FocusScopeStack.pop_back();
6792 }
6793 
SetKeyboardFocusHere(int offset)6794 void ImGui::SetKeyboardFocusHere(int offset)
6795 {
6796     IM_ASSERT(offset >= -1);    // -1 is allowed but not below
6797     ImGuiContext& g = *GImGui;
6798     ImGuiWindow* window = g.CurrentWindow;
6799     g.FocusRequestNextWindow = window;
6800     g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
6801     g.FocusRequestNextCounterTabStop = INT_MAX;
6802 }
6803 
SetItemDefaultFocus()6804 void ImGui::SetItemDefaultFocus()
6805 {
6806     ImGuiContext& g = *GImGui;
6807     ImGuiWindow* window = g.CurrentWindow;
6808     if (!window->Appearing)
6809         return;
6810     if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6811     {
6812         g.NavInitRequest = false;
6813         g.NavInitResultId = g.NavWindow->DC.LastItemId;
6814         g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6815         NavUpdateAnyRequestFlag();
6816         if (!IsItemVisible())
6817             SetScrollHereY();
6818     }
6819 }
6820 
SetStateStorage(ImGuiStorage * tree)6821 void ImGui::SetStateStorage(ImGuiStorage* tree)
6822 {
6823     ImGuiWindow* window = GImGui->CurrentWindow;
6824     window->DC.StateStorage = tree ? tree : &window->StateStorage;
6825 }
6826 
GetStateStorage()6827 ImGuiStorage* ImGui::GetStateStorage()
6828 {
6829     ImGuiWindow* window = GImGui->CurrentWindow;
6830     return window->DC.StateStorage;
6831 }
6832 
PushID(const char * str_id)6833 void ImGui::PushID(const char* str_id)
6834 {
6835     ImGuiContext& g = *GImGui;
6836     ImGuiWindow* window = g.CurrentWindow;
6837     ImGuiID id = window->GetIDNoKeepAlive(str_id);
6838     window->IDStack.push_back(id);
6839 }
6840 
PushID(const char * str_id_begin,const char * str_id_end)6841 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6842 {
6843     ImGuiContext& g = *GImGui;
6844     ImGuiWindow* window = g.CurrentWindow;
6845     ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
6846     window->IDStack.push_back(id);
6847 }
6848 
PushID(const void * ptr_id)6849 void ImGui::PushID(const void* ptr_id)
6850 {
6851     ImGuiContext& g = *GImGui;
6852     ImGuiWindow* window = g.CurrentWindow;
6853     ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
6854     window->IDStack.push_back(id);
6855 }
6856 
PushID(int int_id)6857 void ImGui::PushID(int int_id)
6858 {
6859     ImGuiContext& g = *GImGui;
6860     ImGuiWindow* window = g.CurrentWindow;
6861     ImGuiID id = window->GetIDNoKeepAlive(int_id);
6862     window->IDStack.push_back(id);
6863 }
6864 
6865 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6866 void ImGui::PushOverrideID(ImGuiID id)
6867 {
6868     ImGuiContext& g = *GImGui;
6869     ImGuiWindow* window = g.CurrentWindow;
6870     window->IDStack.push_back(id);
6871 }
6872 
6873 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
6874 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
6875 //  for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
GetIDWithSeed(const char * str,const char * str_end,ImGuiID seed)6876 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
6877 {
6878     ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
6879     ImGui::KeepAliveID(id);
6880 #ifdef IMGUI_ENABLE_TEST_ENGINE
6881     ImGuiContext& g = *GImGui;
6882     IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
6883 #endif
6884     return id;
6885 }
6886 
PopID()6887 void ImGui::PopID()
6888 {
6889     ImGuiWindow* window = GImGui->CurrentWindow;
6890     IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
6891     window->IDStack.pop_back();
6892 }
6893 
GetID(const char * str_id)6894 ImGuiID ImGui::GetID(const char* str_id)
6895 {
6896     ImGuiWindow* window = GImGui->CurrentWindow;
6897     return window->GetID(str_id);
6898 }
6899 
GetID(const char * str_id_begin,const char * str_id_end)6900 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6901 {
6902     ImGuiWindow* window = GImGui->CurrentWindow;
6903     return window->GetID(str_id_begin, str_id_end);
6904 }
6905 
GetID(const void * ptr_id)6906 ImGuiID ImGui::GetID(const void* ptr_id)
6907 {
6908     ImGuiWindow* window = GImGui->CurrentWindow;
6909     return window->GetID(ptr_id);
6910 }
6911 
IsRectVisible(const ImVec2 & size)6912 bool ImGui::IsRectVisible(const ImVec2& size)
6913 {
6914     ImGuiWindow* window = GImGui->CurrentWindow;
6915     return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6916 }
6917 
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6918 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6919 {
6920     ImGuiWindow* window = GImGui->CurrentWindow;
6921     return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6922 }
6923 
6924 
6925 //-----------------------------------------------------------------------------
6926 // [SECTION] ERROR CHECKING
6927 //-----------------------------------------------------------------------------
6928 
6929 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
6930 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
6931 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
6932 // may see different structures than what imgui.cpp sees, which is problematic.
6933 // We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear 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,size_t sz_idx)6934 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, size_t sz_idx)
6935 {
6936     bool error = false;
6937     if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
6938     if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
6939     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
6940     if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
6941     if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
6942     if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
6943     if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
6944     return !error;
6945 }
6946 
ErrorCheckNewFrameSanityChecks()6947 static void ImGui::ErrorCheckNewFrameSanityChecks()
6948 {
6949     ImGuiContext& g = *GImGui;
6950 
6951     // Check user IM_ASSERT macro
6952     // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined!
6953     //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
6954     //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
6955     // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!
6956     // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!
6957     if (true) IM_ASSERT(1); else IM_ASSERT(0);
6958 
6959     // Check user data
6960     // (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)
6961     IM_ASSERT(g.Initialized);
6962     IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
6963     IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
6964     IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
6965     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
6966     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
6967     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
6968     IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f                      && "Invalid style setting!");
6969     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
6970     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
6971     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
6972     for (int n = 0; n < ImGuiKey_COUNT; n++)
6973         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)");
6974 
6975     // 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 added in 1.60 WIP)
6976     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
6977         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
6978 
6979     // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
6980     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
6981         g.IO.ConfigWindowsResizeFromEdges = false;
6982 }
6983 
ErrorCheckEndFrameSanityChecks()6984 static void ImGui::ErrorCheckEndFrameSanityChecks()
6985 {
6986     ImGuiContext& g = *GImGui;
6987 
6988     // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
6989     // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
6990     // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
6991     // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
6992     // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
6993     // while still correctly asserting on mid-frame key press events.
6994     const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
6995     IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
6996     IM_UNUSED(key_mod_flags);
6997 
6998     // Recover from errors
6999     //ErrorCheckEndFrameRecover();
7000 
7001     // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7002     // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7003     if (g.CurrentWindowStack.Size != 1)
7004     {
7005         if (g.CurrentWindowStack.Size > 1)
7006         {
7007             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7008             while (g.CurrentWindowStack.Size > 1)
7009                 End();
7010         }
7011         else
7012         {
7013             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7014         }
7015     }
7016 
7017     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
7018 }
7019 
7020 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
7021 // Must be called during or before EndFrame().
7022 // This is generally flawed as we are not necessarily End/Popping things in the right order.
7023 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
7024 // FIXME: Can't recover from interleaved BeginTabBar/Begin
ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback,void * user_data)7025 void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
7026 {
7027     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
7028     ImGuiContext& g = *GImGui;
7029     while (g.CurrentWindowStack.Size > 0)
7030     {
7031 #ifdef IMGUI_HAS_TABLE
7032         while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
7033         {
7034             if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
7035             EndTable();
7036         }
7037 #endif
7038         ImGuiWindow* window = g.CurrentWindow;
7039         IM_ASSERT(window != NULL);
7040         while (g.CurrentTabBar != NULL) //-V1044
7041         {
7042             if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
7043             EndTabBar();
7044         }
7045         while (window->DC.TreeDepth > 0)
7046         {
7047             if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
7048             TreePop();
7049         }
7050         while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
7051         {
7052             if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
7053             EndGroup();
7054         }
7055         while (window->IDStack.Size > 1)
7056         {
7057             if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
7058             PopID();
7059         }
7060         while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
7061         {
7062             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
7063             PopStyleColor();
7064         }
7065         while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
7066         {
7067             if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
7068             PopStyleVar();
7069         }
7070         while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
7071         {
7072             if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
7073             PopFocusScope();
7074         }
7075         if (g.CurrentWindowStack.Size == 1)
7076         {
7077             IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
7078             break;
7079         }
7080         IM_ASSERT(window == g.CurrentWindow);
7081         if (window->Flags & ImGuiWindowFlags_ChildWindow)
7082         {
7083             if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
7084             EndChild();
7085         }
7086         else
7087         {
7088             if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
7089             End();
7090         }
7091     }
7092 }
7093 
7094 // Save current stack sizes for later compare
SetToCurrentState()7095 void ImGuiStackSizes::SetToCurrentState()
7096 {
7097     ImGuiContext& g = *GImGui;
7098     ImGuiWindow* window = g.CurrentWindow;
7099     SizeOfIDStack = (short)window->IDStack.Size;
7100     SizeOfColorStack = (short)g.ColorStack.Size;
7101     SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
7102     SizeOfFontStack = (short)g.FontStack.Size;
7103     SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
7104     SizeOfGroupStack = (short)g.GroupStack.Size;
7105     SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
7106 }
7107 
7108 // Compare to detect usage errors
CompareWithCurrentState()7109 void ImGuiStackSizes::CompareWithCurrentState()
7110 {
7111     ImGuiContext& g = *GImGui;
7112     ImGuiWindow* window = g.CurrentWindow;
7113     IM_UNUSED(window);
7114 
7115     // Window stacks
7116     // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7117     IM_ASSERT(SizeOfIDStack         == window->IDStack.Size     && "PushID/PopID or TreeNode/TreePop Mismatch!");
7118 
7119     // Global stacks
7120     // 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.
7121     IM_ASSERT(SizeOfGroupStack      == g.GroupStack.Size        && "BeginGroup/EndGroup Mismatch!");
7122     IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size   && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
7123     IM_ASSERT(SizeOfColorStack      >= g.ColorStack.Size        && "PushStyleColor/PopStyleColor Mismatch!");
7124     IM_ASSERT(SizeOfStyleVarStack   >= g.StyleVarStack.Size     && "PushStyleVar/PopStyleVar Mismatch!");
7125     IM_ASSERT(SizeOfFontStack       >= g.FontStack.Size         && "PushFont/PopFont Mismatch!");
7126     IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && "PushFocusScope/PopFocusScope Mismatch!");
7127 }
7128 
7129 
7130 //-----------------------------------------------------------------------------
7131 // [SECTION] LAYOUT
7132 //-----------------------------------------------------------------------------
7133 // - ItemSize()
7134 // - ItemAdd()
7135 // - SameLine()
7136 // - GetCursorScreenPos()
7137 // - SetCursorScreenPos()
7138 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7139 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7140 // - GetCursorStartPos()
7141 // - Indent()
7142 // - Unindent()
7143 // - SetNextItemWidth()
7144 // - PushItemWidth()
7145 // - PushMultiItemsWidths()
7146 // - PopItemWidth()
7147 // - CalcItemWidth()
7148 // - CalcItemSize()
7149 // - GetTextLineHeight()
7150 // - GetTextLineHeightWithSpacing()
7151 // - GetFrameHeight()
7152 // - GetFrameHeightWithSpacing()
7153 // - GetContentRegionMax()
7154 // - GetContentRegionMaxAbs() [Internal]
7155 // - GetContentRegionAvail(),
7156 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
7157 // - GetWindowContentRegionWidth()
7158 // - BeginGroup()
7159 // - EndGroup()
7160 // Also see in imgui_widgets: tab bars, columns.
7161 //-----------------------------------------------------------------------------
7162 
7163 // Advance cursor given item size for layout.
7164 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
7165 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
ItemSize(const ImVec2 & size,float text_baseline_y)7166 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
7167 {
7168     ImGuiContext& g = *GImGui;
7169     ImGuiWindow* window = g.CurrentWindow;
7170     if (window->SkipItems)
7171         return;
7172 
7173     // We increase the height in this function to accommodate for baseline offset.
7174     // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
7175     // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
7176     const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
7177     const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
7178 
7179     // Always align ourselves on pixel boundaries
7180     //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]
7181     window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
7182     window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
7183     window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
7184     window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);        // Next line
7185     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
7186     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
7187     //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
7188 
7189     window->DC.PrevLineSize.y = line_height;
7190     window->DC.CurrLineSize.y = 0.0f;
7191     window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
7192     window->DC.CurrLineTextBaseOffset = 0.0f;
7193 
7194     // Horizontal layout mode
7195     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
7196         SameLine();
7197 }
7198 
ItemSize(const ImRect & bb,float text_baseline_y)7199 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
7200 {
7201     ItemSize(bb.GetSize(), text_baseline_y);
7202 }
7203 
7204 // Declare item bounding box for clipping and interaction.
7205 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
7206 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)7207 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
7208 {
7209     ImGuiContext& g = *GImGui;
7210     ImGuiWindow* window = g.CurrentWindow;
7211 
7212     if (id != 0)
7213     {
7214         // Navigation processing runs prior to clipping early-out
7215         //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
7216         //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
7217         //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
7218         //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
7219         //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
7220         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
7221         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
7222         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
7223         window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
7224         if (g.NavId == id || g.NavAnyRequest)
7225             if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
7226                 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
7227                     NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
7228 
7229         // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
7230 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
7231         if (id == g.DebugItemPickerBreakId)
7232         {
7233             IM_DEBUG_BREAK();
7234             g.DebugItemPickerBreakId = 0;
7235         }
7236 #endif
7237     }
7238 
7239     // Equivalent to calling SetLastItemData()
7240     window->DC.LastItemId = id;
7241     window->DC.LastItemRect = bb;
7242     window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
7243     g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7244 
7245 #ifdef IMGUI_ENABLE_TEST_ENGINE
7246     if (id != 0)
7247         IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7248 #endif
7249 
7250     // Clipping test
7251     const bool is_clipped = IsClippedEx(bb, id, false);
7252     if (is_clipped)
7253         return false;
7254     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7255 
7256     // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7257     if (IsMouseHoveringRect(bb.Min, bb.Max))
7258         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7259     return true;
7260 }
7261 
7262 // Gets back to previous line and continue with horizontal layout
7263 //      offset_from_start_x == 0 : follow right after previous item
7264 //      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7265 //      spacing_w < 0            : use default spacing if pos_x == 0, no spacing if pos_x != 0
7266 //      spacing_w >= 0           : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7267 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7268 {
7269     ImGuiWindow* window = GetCurrentWindow();
7270     if (window->SkipItems)
7271         return;
7272 
7273     ImGuiContext& g = *GImGui;
7274     if (offset_from_start_x != 0.0f)
7275     {
7276         if (spacing_w < 0.0f) spacing_w = 0.0f;
7277         window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7278         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7279     }
7280     else
7281     {
7282         if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7283         window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7284         window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7285     }
7286     window->DC.CurrLineSize = window->DC.PrevLineSize;
7287     window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7288 }
7289 
GetCursorScreenPos()7290 ImVec2 ImGui::GetCursorScreenPos()
7291 {
7292     ImGuiWindow* window = GetCurrentWindowRead();
7293     return window->DC.CursorPos;
7294 }
7295 
SetCursorScreenPos(const ImVec2 & pos)7296 void ImGui::SetCursorScreenPos(const ImVec2& pos)
7297 {
7298     ImGuiWindow* window = GetCurrentWindow();
7299     window->DC.CursorPos = pos;
7300     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7301 }
7302 
7303 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7304 // 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()7305 ImVec2 ImGui::GetCursorPos()
7306 {
7307     ImGuiWindow* window = GetCurrentWindowRead();
7308     return window->DC.CursorPos - window->Pos + window->Scroll;
7309 }
7310 
GetCursorPosX()7311 float ImGui::GetCursorPosX()
7312 {
7313     ImGuiWindow* window = GetCurrentWindowRead();
7314     return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7315 }
7316 
GetCursorPosY()7317 float ImGui::GetCursorPosY()
7318 {
7319     ImGuiWindow* window = GetCurrentWindowRead();
7320     return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7321 }
7322 
SetCursorPos(const ImVec2 & local_pos)7323 void ImGui::SetCursorPos(const ImVec2& local_pos)
7324 {
7325     ImGuiWindow* window = GetCurrentWindow();
7326     window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7327     window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7328 }
7329 
SetCursorPosX(float x)7330 void ImGui::SetCursorPosX(float x)
7331 {
7332     ImGuiWindow* window = GetCurrentWindow();
7333     window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7334     window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7335 }
7336 
SetCursorPosY(float y)7337 void ImGui::SetCursorPosY(float y)
7338 {
7339     ImGuiWindow* window = GetCurrentWindow();
7340     window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7341     window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7342 }
7343 
GetCursorStartPos()7344 ImVec2 ImGui::GetCursorStartPos()
7345 {
7346     ImGuiWindow* window = GetCurrentWindowRead();
7347     return window->DC.CursorStartPos - window->Pos;
7348 }
7349 
Indent(float indent_w)7350 void ImGui::Indent(float indent_w)
7351 {
7352     ImGuiContext& g = *GImGui;
7353     ImGuiWindow* window = GetCurrentWindow();
7354     window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7355     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7356 }
7357 
Unindent(float indent_w)7358 void ImGui::Unindent(float indent_w)
7359 {
7360     ImGuiContext& g = *GImGui;
7361     ImGuiWindow* window = GetCurrentWindow();
7362     window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7363     window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7364 }
7365 
7366 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)7367 void ImGui::SetNextItemWidth(float item_width)
7368 {
7369     ImGuiContext& g = *GImGui;
7370     g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
7371     g.NextItemData.Width = item_width;
7372 }
7373 
PushItemWidth(float item_width)7374 void ImGui::PushItemWidth(float item_width)
7375 {
7376     ImGuiContext& g = *GImGui;
7377     ImGuiWindow* window = g.CurrentWindow;
7378     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
7379     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
7380     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7381 }
7382 
PushMultiItemsWidths(int components,float w_full)7383 void ImGui::PushMultiItemsWidths(int components, float w_full)
7384 {
7385     ImGuiContext& g = *GImGui;
7386     ImGuiWindow* window = g.CurrentWindow;
7387     const ImGuiStyle& style = g.Style;
7388     const float w_item_one  = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
7389     const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
7390     window->DC.ItemWidthStack.push_back(w_item_last);
7391     for (int i = 0; i < components - 1; i++)
7392         window->DC.ItemWidthStack.push_back(w_item_one);
7393     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
7394     g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7395 }
7396 
PopItemWidth()7397 void ImGui::PopItemWidth()
7398 {
7399     ImGuiWindow* window = GetCurrentWindow();
7400     window->DC.ItemWidthStack.pop_back();
7401     window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
7402 }
7403 
7404 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
7405 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()7406 float ImGui::CalcItemWidth()
7407 {
7408     ImGuiContext& g = *GImGui;
7409     ImGuiWindow* window = g.CurrentWindow;
7410     float w;
7411     if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
7412         w = g.NextItemData.Width;
7413     else
7414         w = window->DC.ItemWidth;
7415     if (w < 0.0f)
7416     {
7417         float region_max_x = GetContentRegionMaxAbs().x;
7418         w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
7419     }
7420     w = IM_FLOOR(w);
7421     return w;
7422 }
7423 
7424 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
7425 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
7426 // Note that only CalcItemWidth() is publicly exposed.
7427 // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
CalcItemSize(ImVec2 size,float default_w,float default_h)7428 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
7429 {
7430     ImGuiWindow* window = GImGui->CurrentWindow;
7431 
7432     ImVec2 region_max;
7433     if (size.x < 0.0f || size.y < 0.0f)
7434         region_max = GetContentRegionMaxAbs();
7435 
7436     if (size.x == 0.0f)
7437         size.x = default_w;
7438     else if (size.x < 0.0f)
7439         size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
7440 
7441     if (size.y == 0.0f)
7442         size.y = default_h;
7443     else if (size.y < 0.0f)
7444         size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
7445 
7446     return size;
7447 }
7448 
GetTextLineHeight()7449 float ImGui::GetTextLineHeight()
7450 {
7451     ImGuiContext& g = *GImGui;
7452     return g.FontSize;
7453 }
7454 
GetTextLineHeightWithSpacing()7455 float ImGui::GetTextLineHeightWithSpacing()
7456 {
7457     ImGuiContext& g = *GImGui;
7458     return g.FontSize + g.Style.ItemSpacing.y;
7459 }
7460 
GetFrameHeight()7461 float ImGui::GetFrameHeight()
7462 {
7463     ImGuiContext& g = *GImGui;
7464     return g.FontSize + g.Style.FramePadding.y * 2.0f;
7465 }
7466 
GetFrameHeightWithSpacing()7467 float ImGui::GetFrameHeightWithSpacing()
7468 {
7469     ImGuiContext& g = *GImGui;
7470     return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7471 }
7472 
7473 // FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience!
7474 
7475 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()7476 ImVec2 ImGui::GetContentRegionMax()
7477 {
7478     ImGuiContext& g = *GImGui;
7479     ImGuiWindow* window = g.CurrentWindow;
7480     ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
7481     if (window->DC.CurrentColumns || g.CurrentTable)
7482         mx.x = window->WorkRect.Max.x - window->Pos.x;
7483     return mx;
7484 }
7485 
7486 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()7487 ImVec2 ImGui::GetContentRegionMaxAbs()
7488 {
7489     ImGuiContext& g = *GImGui;
7490     ImGuiWindow* window = g.CurrentWindow;
7491     ImVec2 mx = window->ContentRegionRect.Max;
7492     if (window->DC.CurrentColumns || g.CurrentTable)
7493         mx.x = window->WorkRect.Max.x;
7494     return mx;
7495 }
7496 
GetContentRegionAvail()7497 ImVec2 ImGui::GetContentRegionAvail()
7498 {
7499     ImGuiWindow* window = GImGui->CurrentWindow;
7500     return GetContentRegionMaxAbs() - window->DC.CursorPos;
7501 }
7502 
7503 // In window space (not screen space!)
GetWindowContentRegionMin()7504 ImVec2 ImGui::GetWindowContentRegionMin()
7505 {
7506     ImGuiWindow* window = GImGui->CurrentWindow;
7507     return window->ContentRegionRect.Min - window->Pos;
7508 }
7509 
GetWindowContentRegionMax()7510 ImVec2 ImGui::GetWindowContentRegionMax()
7511 {
7512     ImGuiWindow* window = GImGui->CurrentWindow;
7513     return window->ContentRegionRect.Max - window->Pos;
7514 }
7515 
GetWindowContentRegionWidth()7516 float ImGui::GetWindowContentRegionWidth()
7517 {
7518     ImGuiWindow* window = GImGui->CurrentWindow;
7519     return window->ContentRegionRect.GetWidth();
7520 }
7521 
7522 // 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.)
7523 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
BeginGroup()7524 void ImGui::BeginGroup()
7525 {
7526     ImGuiContext& g = *GImGui;
7527     ImGuiWindow* window = g.CurrentWindow;
7528 
7529     g.GroupStack.resize(g.GroupStack.Size + 1);
7530     ImGuiGroupData& group_data = g.GroupStack.back();
7531     group_data.WindowID = window->ID;
7532     group_data.BackupCursorPos = window->DC.CursorPos;
7533     group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7534     group_data.BackupIndent = window->DC.Indent;
7535     group_data.BackupGroupOffset = window->DC.GroupOffset;
7536     group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7537     group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7538     group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7539     group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7540     group_data.EmitItem = true;
7541 
7542     window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7543     window->DC.Indent = window->DC.GroupOffset;
7544     window->DC.CursorMaxPos = window->DC.CursorPos;
7545     window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7546     if (g.LogEnabled)
7547         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7548 }
7549 
EndGroup()7550 void ImGui::EndGroup()
7551 {
7552     ImGuiContext& g = *GImGui;
7553     ImGuiWindow* window = g.CurrentWindow;
7554     IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
7555 
7556     ImGuiGroupData& group_data = g.GroupStack.back();
7557     IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
7558 
7559     ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7560 
7561     window->DC.CursorPos = group_data.BackupCursorPos;
7562     window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7563     window->DC.Indent = group_data.BackupIndent;
7564     window->DC.GroupOffset = group_data.BackupGroupOffset;
7565     window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7566     window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7567     if (g.LogEnabled)
7568         g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7569 
7570     if (!group_data.EmitItem)
7571     {
7572         g.GroupStack.pop_back();
7573         return;
7574     }
7575 
7576     window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset);      // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
7577     ItemSize(group_bb.GetSize());
7578     ItemAdd(group_bb, 0);
7579 
7580     // 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.
7581     // 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.
7582     // Also if you grep for LastItemId you'll notice it is only used in that context.
7583     // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
7584     const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7585     const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
7586     if (group_contains_curr_active_id)
7587         window->DC.LastItemId = g.ActiveId;
7588     else if (group_contains_prev_active_id)
7589         window->DC.LastItemId = g.ActiveIdPreviousFrame;
7590     window->DC.LastItemRect = group_bb;
7591 
7592     // Forward Edited flag
7593     if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7594         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
7595 
7596     // Forward Deactivated flag
7597     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7598     if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7599         window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
7600 
7601     g.GroupStack.pop_back();
7602     //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
7603 }
7604 
7605 
7606 //-----------------------------------------------------------------------------
7607 // [SECTION] SCROLLING
7608 //-----------------------------------------------------------------------------
7609 
7610 // Helper to snap on edges when aiming at an item very close to the edge,
7611 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
7612 // When we refactor the scrolling API this may be configurable with a flag?
7613 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
CalcScrollEdgeSnap(float target,float snap_min,float snap_max,float snap_threshold,float center_ratio)7614 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
7615 {
7616     if (target <= snap_min + snap_threshold)
7617         return ImLerp(snap_min, target, center_ratio);
7618     if (target >= snap_max - snap_threshold)
7619         return ImLerp(target, snap_max, center_ratio);
7620     return target;
7621 }
7622 
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)7623 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
7624 {
7625     ImVec2 scroll = window->Scroll;
7626     if (window->ScrollTarget.x < FLT_MAX)
7627     {
7628         float center_x_ratio = window->ScrollTargetCenterRatio.x;
7629         float scroll_target_x = window->ScrollTarget.x;
7630         float snap_x_min = 0.0f;
7631         float snap_x_max = window->ScrollMax.x + window->Size.x;
7632         if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
7633             scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
7634         scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x);
7635     }
7636     if (window->ScrollTarget.y < FLT_MAX)
7637     {
7638         float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7639         float center_y_ratio = window->ScrollTargetCenterRatio.y;
7640         float scroll_target_y = window->ScrollTarget.y;
7641         float snap_y_min = 0.0f;
7642         float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height;
7643         if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
7644             scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
7645         scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7646     }
7647     scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
7648     scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
7649     if (!window->Collapsed && !window->SkipItems)
7650     {
7651         scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7652         scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7653     }
7654     return scroll;
7655 }
7656 
7657 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7658 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7659 {
7660     ImGuiContext& g = *GImGui;
7661     ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7662     //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7663 
7664     ImVec2 delta_scroll;
7665     if (!window_rect.Contains(item_rect))
7666     {
7667         if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7668             SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
7669         else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7670             SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7671         if (item_rect.Min.y < window_rect.Min.y)
7672             SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7673         else if (item_rect.Max.y >= window_rect.Max.y)
7674             SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7675 
7676         ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7677         delta_scroll = next_scroll - window->Scroll;
7678     }
7679 
7680     // Also scroll parent window to keep us into view if necessary
7681     if (window->Flags & ImGuiWindowFlags_ChildWindow)
7682         delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7683 
7684     return delta_scroll;
7685 }
7686 
GetScrollX()7687 float ImGui::GetScrollX()
7688 {
7689     ImGuiWindow* window = GImGui->CurrentWindow;
7690     return window->Scroll.x;
7691 }
7692 
GetScrollY()7693 float ImGui::GetScrollY()
7694 {
7695     ImGuiWindow* window = GImGui->CurrentWindow;
7696     return window->Scroll.y;
7697 }
7698 
GetScrollMaxX()7699 float ImGui::GetScrollMaxX()
7700 {
7701     ImGuiWindow* window = GImGui->CurrentWindow;
7702     return window->ScrollMax.x;
7703 }
7704 
GetScrollMaxY()7705 float ImGui::GetScrollMaxY()
7706 {
7707     ImGuiWindow* window = GImGui->CurrentWindow;
7708     return window->ScrollMax.y;
7709 }
7710 
SetScrollX(ImGuiWindow * window,float scroll_x)7711 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
7712 {
7713     window->ScrollTarget.x = scroll_x;
7714     window->ScrollTargetCenterRatio.x = 0.0f;
7715     window->ScrollTargetEdgeSnapDist.x = 0.0f;
7716 }
7717 
SetScrollY(ImGuiWindow * window,float scroll_y)7718 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
7719 {
7720     window->ScrollTarget.y = scroll_y;
7721     window->ScrollTargetCenterRatio.y = 0.0f;
7722     window->ScrollTargetEdgeSnapDist.y = 0.0f;
7723 }
7724 
SetScrollX(float scroll_x)7725 void ImGui::SetScrollX(float scroll_x)
7726 {
7727     ImGuiContext& g = *GImGui;
7728     SetScrollX(g.CurrentWindow, scroll_x);
7729 }
7730 
SetScrollY(float scroll_y)7731 void ImGui::SetScrollY(float scroll_y)
7732 {
7733     ImGuiContext& g = *GImGui;
7734     SetScrollY(g.CurrentWindow, scroll_y);
7735 }
7736 
7737 // Note that a local position will vary depending on initial scroll value,
7738 // This is a little bit confusing so bear with us:
7739 //  - local_pos = (absolution_pos - window->Pos)
7740 //  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
7741 //    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
7742 //  - They mostly exists because of legacy API.
7743 // Following the rules above, when trying to work with scrolling code, consider that:
7744 //  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
7745 //  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
7746 // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7747 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7748 {
7749     IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7750     window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
7751     window->ScrollTargetCenterRatio.x = center_x_ratio;
7752     window->ScrollTargetEdgeSnapDist.x = 0.0f;
7753 }
7754 
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7755 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7756 {
7757     IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7758     local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect
7759     window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
7760     window->ScrollTargetCenterRatio.y = center_y_ratio;
7761     window->ScrollTargetEdgeSnapDist.y = 0.0f;
7762 }
7763 
SetScrollFromPosX(float local_x,float center_x_ratio)7764 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7765 {
7766     ImGuiContext& g = *GImGui;
7767     SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7768 }
7769 
SetScrollFromPosY(float local_y,float center_y_ratio)7770 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7771 {
7772     ImGuiContext& g = *GImGui;
7773     SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7774 }
7775 
7776 // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
SetScrollHereX(float center_x_ratio)7777 void ImGui::SetScrollHereX(float center_x_ratio)
7778 {
7779     ImGuiContext& g = *GImGui;
7780     ImGuiWindow* window = g.CurrentWindow;
7781     float spacing_x = g.Style.ItemSpacing.x;
7782     float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio);
7783     SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
7784 
7785     // Tweak: snap on edges when aiming at an item very close to the edge
7786     window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
7787 }
7788 
7789 // 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)7790 void ImGui::SetScrollHereY(float center_y_ratio)
7791 {
7792     ImGuiContext& g = *GImGui;
7793     ImGuiWindow* window = g.CurrentWindow;
7794     float spacing_y = g.Style.ItemSpacing.y;
7795     float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
7796     SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
7797 
7798     // Tweak: snap on edges when aiming at an item very close to the edge
7799     window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
7800 }
7801 
7802 //-----------------------------------------------------------------------------
7803 // [SECTION] TOOLTIPS
7804 //-----------------------------------------------------------------------------
7805 
BeginTooltip()7806 void ImGui::BeginTooltip()
7807 {
7808     BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
7809 }
7810 
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)7811 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
7812 {
7813     ImGuiContext& g = *GImGui;
7814 
7815     if (g.DragDropWithinSource || g.DragDropWithinTarget)
7816     {
7817         // 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)
7818         // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7819         // 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.
7820         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7821         ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7822         SetNextWindowPos(tooltip_pos);
7823         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7824         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7825         tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
7826     }
7827 
7828     char window_name[16];
7829     ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7830     if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
7831         if (ImGuiWindow* window = FindWindowByName(window_name))
7832             if (window->Active)
7833             {
7834                 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7835                 window->Hidden = true;
7836                 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
7837                 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7838             }
7839     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
7840     Begin(window_name, NULL, flags | extra_flags);
7841 }
7842 
EndTooltip()7843 void ImGui::EndTooltip()
7844 {
7845     IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
7846     End();
7847 }
7848 
SetTooltipV(const char * fmt,va_list args)7849 void ImGui::SetTooltipV(const char* fmt, va_list args)
7850 {
7851     BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
7852     TextV(fmt, args);
7853     EndTooltip();
7854 }
7855 
SetTooltip(const char * fmt,...)7856 void ImGui::SetTooltip(const char* fmt, ...)
7857 {
7858     va_list args;
7859     va_start(args, fmt);
7860     SetTooltipV(fmt, args);
7861     va_end(args);
7862 }
7863 
7864 //-----------------------------------------------------------------------------
7865 // [SECTION] POPUPS
7866 //-----------------------------------------------------------------------------
7867 
7868 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)7869 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
7870 {
7871     ImGuiContext& g = *GImGui;
7872     if (popup_flags & ImGuiPopupFlags_AnyPopupId)
7873     {
7874         // Return true if any popup is open at the current BeginPopup() level of the popup stack
7875         // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
7876         IM_ASSERT(id == 0);
7877         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
7878             return g.OpenPopupStack.Size > 0;
7879         else
7880             return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
7881     }
7882     else
7883     {
7884         if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
7885         {
7886             // Return true if the popup is open anywhere in the popup stack
7887             for (int n = 0; n < g.OpenPopupStack.Size; n++)
7888                 if (g.OpenPopupStack[n].PopupId == id)
7889                     return true;
7890             return false;
7891         }
7892         else
7893         {
7894             // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
7895             return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7896         }
7897     }
7898 }
7899 
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)7900 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
7901 {
7902     ImGuiContext& g = *GImGui;
7903     ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
7904     if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
7905         IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
7906     return IsPopupOpen(id, popup_flags);
7907 }
7908 
GetTopMostPopupModal()7909 ImGuiWindow* ImGui::GetTopMostPopupModal()
7910 {
7911     ImGuiContext& g = *GImGui;
7912     for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
7913         if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7914             if (popup->Flags & ImGuiWindowFlags_Modal)
7915                 return popup;
7916     return NULL;
7917 }
7918 
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)7919 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
7920 {
7921     ImGuiContext& g = *GImGui;
7922     OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
7923 }
7924 
7925 // Mark popup as open (toggle toward open state).
7926 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7927 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7928 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags)7929 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
7930 {
7931     ImGuiContext& g = *GImGui;
7932     ImGuiWindow* parent_window = g.CurrentWindow;
7933     const int current_stack_size = g.BeginPopupStack.Size;
7934 
7935     if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
7936         if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
7937             return;
7938 
7939     ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7940     popup_ref.PopupId = id;
7941     popup_ref.Window = NULL;
7942     popup_ref.SourceWindow = g.NavWindow;
7943     popup_ref.OpenFrameCount = g.FrameCount;
7944     popup_ref.OpenParentId = parent_window->IDStack.back();
7945     popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7946     popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7947 
7948     IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
7949     if (g.OpenPopupStack.Size < current_stack_size + 1)
7950     {
7951         g.OpenPopupStack.push_back(popup_ref);
7952     }
7953     else
7954     {
7955         // 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
7956         // 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
7957         // 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.
7958         if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7959         {
7960             g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7961         }
7962         else
7963         {
7964             // Close child popups if any, then flag popup for open/reopen
7965             ClosePopupToLevel(current_stack_size, false);
7966             g.OpenPopupStack.push_back(popup_ref);
7967         }
7968 
7969         // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7970         // This is equivalent to what ClosePopupToLevel() does.
7971         //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7972         //    FocusWindow(parent_window);
7973     }
7974 }
7975 
7976 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7977 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7978 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7979 {
7980     ImGuiContext& g = *GImGui;
7981     if (g.OpenPopupStack.Size == 0)
7982         return;
7983 
7984     // Don't close our own child popup windows.
7985     int popup_count_to_keep = 0;
7986     if (ref_window)
7987     {
7988         // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7989         for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7990         {
7991             ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7992             if (!popup.Window)
7993                 continue;
7994             IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7995             if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7996                 continue;
7997 
7998             // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
7999             // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8000             //     Window -> Popup1 -> Popup2 -> Popup3
8001             // - Each popups may contain child windows, which is why we compare ->RootWindow!
8002             //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8003             bool ref_window_is_descendent_of_popup = false;
8004             for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8005                 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8006                     if (popup_window->RootWindow == ref_window->RootWindow)
8007                     {
8008                         ref_window_is_descendent_of_popup = true;
8009                         break;
8010                     }
8011             if (!ref_window_is_descendent_of_popup)
8012                 break;
8013         }
8014     }
8015     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
8016     {
8017         IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8018         ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8019     }
8020 }
8021 
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)8022 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8023 {
8024     ImGuiContext& g = *GImGui;
8025     IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8026     IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8027 
8028     // Trim open popup stack
8029     ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8030     ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8031     g.OpenPopupStack.resize(remaining);
8032 
8033     if (restore_focus_to_window_under_popup)
8034     {
8035         if (focus_window && !focus_window->WasActive && popup_window)
8036         {
8037             // Fallback
8038             FocusTopMostWindowUnderOne(popup_window, NULL);
8039         }
8040         else
8041         {
8042             if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8043                 focus_window = NavRestoreLastChildNavWindow(focus_window);
8044             FocusWindow(focus_window);
8045         }
8046     }
8047 }
8048 
8049 // Close the popup we have begin-ed into.
CloseCurrentPopup()8050 void ImGui::CloseCurrentPopup()
8051 {
8052     ImGuiContext& g = *GImGui;
8053     int popup_idx = g.BeginPopupStack.Size - 1;
8054     if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8055         return;
8056 
8057     // Closing a menu closes its top-most parent popup (unless a modal)
8058     while (popup_idx > 0)
8059     {
8060         ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8061         ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8062         bool close_parent = false;
8063         if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8064             if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8065                 close_parent = true;
8066         if (!close_parent)
8067             break;
8068         popup_idx--;
8069     }
8070     IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8071     ClosePopupToLevel(popup_idx, true);
8072 
8073     // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8074     // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8075     // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8076     if (ImGuiWindow* window = g.NavWindow)
8077         window->DC.NavHideHighlightOneFrame = true;
8078 }
8079 
8080 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)8081 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8082 {
8083     ImGuiContext& g = *GImGui;
8084     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8085     {
8086         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8087         return false;
8088     }
8089 
8090     char name[20];
8091     if (flags & ImGuiWindowFlags_ChildMenu)
8092         ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8093     else
8094         ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8095 
8096     flags |= ImGuiWindowFlags_Popup;
8097     bool is_open = Begin(name, NULL, flags);
8098     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8099         EndPopup();
8100 
8101     return is_open;
8102 }
8103 
BeginPopup(const char * str_id,ImGuiWindowFlags flags)8104 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8105 {
8106     ImGuiContext& g = *GImGui;
8107     if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8108     {
8109         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8110         return false;
8111     }
8112     flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8113     return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8114 }
8115 
8116 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8117 // Note that popup visibility status is owned by Dear 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)8118 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8119 {
8120     ImGuiContext& g = *GImGui;
8121     ImGuiWindow* window = g.CurrentWindow;
8122     const ImGuiID id = window->GetID(name);
8123     if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8124     {
8125         g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8126         return false;
8127     }
8128 
8129     // Center modal windows by default for increased visibility
8130     // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
8131     // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
8132     if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
8133         SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
8134 
8135     flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
8136     const bool is_open = Begin(name, p_open, flags);
8137     if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8138     {
8139         EndPopup();
8140         if (is_open)
8141             ClosePopupToLevel(g.BeginPopupStack.Size, true);
8142         return false;
8143     }
8144     return is_open;
8145 }
8146 
EndPopup()8147 void ImGui::EndPopup()
8148 {
8149     ImGuiContext& g = *GImGui;
8150     ImGuiWindow* window = g.CurrentWindow;
8151     IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
8152     IM_ASSERT(g.BeginPopupStack.Size > 0);
8153 
8154     // Make all menus and popups wrap around for now, may need to expose that policy.
8155     if (g.NavWindow == window)
8156         NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
8157 
8158     // Child-popups don't need to be laid out
8159     IM_ASSERT(g.WithinEndChild == false);
8160     if (window->Flags & ImGuiWindowFlags_ChildWindow)
8161         g.WithinEndChild = true;
8162     End();
8163     g.WithinEndChild = false;
8164 }
8165 
8166 // Helper to open a popup if mouse button is released over the item
8167 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
OpenPopupOnItemClick(const char * str_id,ImGuiPopupFlags popup_flags)8168 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
8169 {
8170     ImGuiWindow* window = GImGui->CurrentWindow;
8171     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8172     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8173     {
8174         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!
8175         IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8176         OpenPopupEx(id, popup_flags);
8177     }
8178 }
8179 
8180 // This is a helper to handle the simplest case of associating one named popup to one given widget.
8181 // - You can pass a NULL str_id to use the identifier of the last item.
8182 // - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
8183 // - This is essentially the same as calling OpenPopupOnItemClick() + BeginPopup() but written to avoid
8184 //   computing the ID twice because BeginPopupContextXXX functions may be called very frequently.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)8185 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8186 {
8187     ImGuiWindow* window = GImGui->CurrentWindow;
8188     if (window->SkipItems)
8189         return false;
8190     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!
8191     IM_ASSERT(id != 0);                                                  // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8192     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8193     if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8194         OpenPopupEx(id, popup_flags);
8195     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8196 }
8197 
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)8198 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
8199 {
8200     ImGuiWindow* window = GImGui->CurrentWindow;
8201     if (!str_id)
8202         str_id = "window_context";
8203     ImGuiID id = window->GetID(str_id);
8204     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8205     if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8206         if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
8207             OpenPopupEx(id, popup_flags);
8208     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8209 }
8210 
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)8211 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
8212 {
8213     ImGuiWindow* window = GImGui->CurrentWindow;
8214     if (!str_id)
8215         str_id = "void_context";
8216     ImGuiID id = window->GetID(str_id);
8217     int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8218     if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
8219         if (GetTopMostPopupModal() == NULL)
8220             OpenPopupEx(id, popup_flags);
8221     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8222 }
8223 
8224 // 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.)
8225 // 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)8226 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
8227 {
8228     ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
8229     //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
8230     //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
8231 
8232     // Combo Box policy (we want a connecting edge)
8233     if (policy == ImGuiPopupPositionPolicy_ComboBox)
8234     {
8235         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
8236         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8237         {
8238             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8239             if (n != -1 && dir == *last_dir) // Already tried this direction?
8240                 continue;
8241             ImVec2 pos;
8242             if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
8243             if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8244             if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8245             if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8246             if (!r_outer.Contains(ImRect(pos, pos + size)))
8247                 continue;
8248             *last_dir = dir;
8249             return pos;
8250         }
8251     }
8252 
8253     // Tooltip and Default popup policy
8254     // (Always first try the direction we used on the last frame, if any)
8255     if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
8256     {
8257         const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8258         for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8259         {
8260             const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8261             if (n != -1 && dir == *last_dir) // Already tried this direction?
8262                 continue;
8263 
8264             const 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);
8265             const 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);
8266 
8267             // If there not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
8268             if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
8269                 continue;
8270             if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
8271                 continue;
8272 
8273             ImVec2 pos;
8274             pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
8275             pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
8276 
8277             // Clamp top-left corner of popup
8278             pos.x = ImMax(pos.x, r_outer.Min.x);
8279             pos.y = ImMax(pos.y, r_outer.Min.y);
8280 
8281             *last_dir = dir;
8282             return pos;
8283         }
8284     }
8285 
8286     // Fallback when not enough room:
8287     *last_dir = ImGuiDir_None;
8288 
8289     // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
8290     if (policy == ImGuiPopupPositionPolicy_Tooltip)
8291         return ref_pos + ImVec2(2, 2);
8292 
8293     // Otherwise try to keep within display
8294     ImVec2 pos = ref_pos;
8295     pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
8296     pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
8297     return pos;
8298 }
8299 
GetWindowAllowedExtentRect(ImGuiWindow * window)8300 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
8301 {
8302     IM_UNUSED(window);
8303     ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
8304     ImRect r_screen = GetViewportRect();
8305     r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
8306     return r_screen;
8307 }
8308 
FindBestWindowPosForPopup(ImGuiWindow * window)8309 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
8310 {
8311     ImGuiContext& g = *GImGui;
8312 
8313     ImRect r_outer = GetWindowAllowedExtentRect(window);
8314     if (window->Flags & ImGuiWindowFlags_ChildMenu)
8315     {
8316         // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
8317         // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
8318         IM_ASSERT(g.CurrentWindow == window);
8319         ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
8320         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).
8321         ImRect r_avoid;
8322         if (parent_window->DC.MenuBarAppending)
8323             r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
8324         else
8325             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);
8326         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8327     }
8328     if (window->Flags & ImGuiWindowFlags_Popup)
8329     {
8330         ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
8331         return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8332     }
8333     if (window->Flags & ImGuiWindowFlags_Tooltip)
8334     {
8335         // Position tooltip (always follows mouse)
8336         float sc = g.Style.MouseCursorScale;
8337         ImVec2 ref_pos = NavCalcPreferredRefPos();
8338         ImRect r_avoid;
8339         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
8340             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
8341         else
8342             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.
8343         return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
8344     }
8345     IM_ASSERT(0);
8346     return window->Pos;
8347 }
8348 
8349 //-----------------------------------------------------------------------------
8350 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
8351 //-----------------------------------------------------------------------------
8352 
8353 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing,
8354 // and needs some explanation or serious refactoring.
SetNavID(ImGuiID id,int nav_layer,ImGuiID focus_scope_id)8355 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id)
8356 {
8357     ImGuiContext& g = *GImGui;
8358     IM_ASSERT(g.NavWindow);
8359     IM_ASSERT(nav_layer == 0 || nav_layer == 1);
8360     g.NavId = id;
8361     g.NavFocusScopeId = focus_scope_id;
8362     g.NavWindow->NavLastIds[nav_layer] = id;
8363 }
8364 
SetNavIDWithRectRel(ImGuiID id,int nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)8365 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
8366 {
8367     ImGuiContext& g = *GImGui;
8368     SetNavID(id, nav_layer, focus_scope_id);
8369     g.NavWindow->NavRectRel[nav_layer] = rect_rel;
8370     g.NavMousePosDirty = true;
8371     g.NavDisableHighlight = false;
8372     g.NavDisableMouseHover = true;
8373 }
8374 
SetFocusID(ImGuiID id,ImGuiWindow * window)8375 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
8376 {
8377     ImGuiContext& g = *GImGui;
8378     IM_ASSERT(id != 0);
8379 
8380     // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
8381     // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
8382     const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
8383     if (g.NavWindow != window)
8384         g.NavInitRequest = false;
8385     g.NavWindow = window;
8386     g.NavId = id;
8387     g.NavLayer = nav_layer;
8388     g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8389     window->NavLastIds[nav_layer] = id;
8390     if (window->DC.LastItemId == id)
8391         window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
8392 
8393     if (g.ActiveIdSource == ImGuiInputSource_Nav)
8394         g.NavDisableMouseHover = true;
8395     else
8396         g.NavDisableHighlight = true;
8397 }
8398 
ImGetDirQuadrantFromDelta(float dx,float dy)8399 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
8400 {
8401     if (ImFabs(dx) > ImFabs(dy))
8402         return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
8403     return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
8404 }
8405 
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)8406 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
8407 {
8408     if (a1 < b0)
8409         return a1 - b0;
8410     if (b1 < a0)
8411         return a0 - b1;
8412     return 0.0f;
8413 }
8414 
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)8415 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
8416 {
8417     if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
8418     {
8419         r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
8420         r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
8421     }
8422     else
8423     {
8424         r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
8425         r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
8426     }
8427 }
8428 
8429 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)8430 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
8431 {
8432     ImGuiContext& g = *GImGui;
8433     ImGuiWindow* window = g.CurrentWindow;
8434     if (g.NavLayer != window->DC.NavLayerCurrent)
8435         return false;
8436 
8437     const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
8438     g.NavScoringCount++;
8439 
8440     // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
8441     if (window->ParentWindow == g.NavWindow)
8442     {
8443         IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
8444         if (!window->ClipRect.Overlaps(cand))
8445             return false;
8446         cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
8447     }
8448 
8449     // 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)
8450     // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
8451     NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
8452 
8453     // Compute distance between boxes
8454     // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
8455     float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
8456     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
8457     if (dby != 0.0f && dbx != 0.0f)
8458         dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
8459     float dist_box = ImFabs(dbx) + ImFabs(dby);
8460 
8461     // 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)
8462     float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
8463     float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
8464     float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
8465 
8466     // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
8467     ImGuiDir quadrant;
8468     float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
8469     if (dbx != 0.0f || dby != 0.0f)
8470     {
8471         // For non-overlapping boxes, use distance between boxes
8472         dax = dbx;
8473         day = dby;
8474         dist_axial = dist_box;
8475         quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
8476     }
8477     else if (dcx != 0.0f || dcy != 0.0f)
8478     {
8479         // For overlapping boxes with different centers, use distance between centers
8480         dax = dcx;
8481         day = dcy;
8482         dist_axial = dist_center;
8483         quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
8484     }
8485     else
8486     {
8487         // 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)
8488         quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
8489     }
8490 
8491 #if IMGUI_DEBUG_NAV_SCORING
8492     char buf[128];
8493     if (IsMouseHoveringRect(cand.Min, cand.Max))
8494     {
8495         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]);
8496         ImDrawList* draw_list = GetForegroundDrawList(window);
8497         draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
8498         draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
8499         draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
8500         draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
8501     }
8502     else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
8503     {
8504         if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
8505         if (quadrant == g.NavMoveDir)
8506         {
8507             ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
8508             ImDrawList* draw_list = GetForegroundDrawList(window);
8509             draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
8510             draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
8511         }
8512     }
8513 #endif
8514 
8515     // Is it in the quadrant we're interesting in moving to?
8516     bool new_best = false;
8517     if (quadrant == g.NavMoveDir)
8518     {
8519         // Does it beat the current best candidate?
8520         if (dist_box < result->DistBox)
8521         {
8522             result->DistBox = dist_box;
8523             result->DistCenter = dist_center;
8524             return true;
8525         }
8526         if (dist_box == result->DistBox)
8527         {
8528             // Try using distance between center points to break ties
8529             if (dist_center < result->DistCenter)
8530             {
8531                 result->DistCenter = dist_center;
8532                 new_best = true;
8533             }
8534             else if (dist_center == result->DistCenter)
8535             {
8536                 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8537                 // (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),
8538                 // 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.
8539                 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8540                     new_best = true;
8541             }
8542         }
8543     }
8544 
8545     // 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
8546     // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8547     // 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.
8548     // 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.
8549     // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8550     if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
8551         if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8552             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))
8553             {
8554                 result->DistAxial = dist_axial;
8555                 new_best = true;
8556             }
8557 
8558     return new_best;
8559 }
8560 
NavApplyItemToResult(ImGuiNavMoveResult * result,ImGuiWindow * window,ImGuiID id,const ImRect & nav_bb_rel)8561 static void ImGui::NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
8562 {
8563     result->Window = window;
8564     result->ID = id;
8565     result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8566     result->RectRel = nav_bb_rel;
8567 }
8568 
8569 // 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)8570 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8571 {
8572     ImGuiContext& g = *GImGui;
8573     //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.
8574     //    return;
8575 
8576     const ImGuiItemFlags item_flags = window->DC.ItemFlags;
8577     const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8578 
8579     // Process Init Request
8580     if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8581     {
8582         // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8583         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
8584         {
8585             g.NavInitResultId = id;
8586             g.NavInitResultRectRel = nav_bb_rel;
8587         }
8588         if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
8589         {
8590             g.NavInitRequest = false; // Found a match, clear request
8591             NavUpdateAnyRequestFlag();
8592         }
8593     }
8594 
8595     // Process Move Request (scoring for navigation)
8596     // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
8597     if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
8598     {
8599         ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8600 #if IMGUI_DEBUG_NAV_SCORING
8601         // [DEBUG] Score all items in NavWindow at all times
8602         if (!g.NavMoveRequest)
8603             g.NavMoveDir = g.NavMoveDirLast;
8604         bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
8605 #else
8606         bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
8607 #endif
8608         if (new_best)
8609             NavApplyItemToResult(result, window, id, nav_bb_rel);
8610 
8611         // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8612         const float VISIBLE_RATIO = 0.70f;
8613         if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8614             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)
8615                 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
8616                     NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel);
8617     }
8618 
8619     // Update window-relative bounding box of navigated item
8620     if (g.NavId == id)
8621     {
8622         g.NavWindow = window;                                           // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8623         g.NavLayer = window->DC.NavLayerCurrent;
8624         g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8625         g.NavIdIsAlive = true;
8626         g.NavIdTabCounter = window->DC.FocusCounterTabStop;
8627         window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel;    // Store item bounding box (relative to window position)
8628     }
8629 }
8630 
NavMoveRequestButNoResultYet()8631 bool ImGui::NavMoveRequestButNoResultYet()
8632 {
8633     ImGuiContext& g = *GImGui;
8634     return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8635 }
8636 
NavMoveRequestCancel()8637 void ImGui::NavMoveRequestCancel()
8638 {
8639     ImGuiContext& g = *GImGui;
8640     g.NavMoveRequest = false;
8641     NavUpdateAnyRequestFlag();
8642 }
8643 
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)8644 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
8645 {
8646     ImGuiContext& g = *GImGui;
8647     IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
8648     NavMoveRequestCancel();
8649     g.NavMoveDir = move_dir;
8650     g.NavMoveClipDir = clip_dir;
8651     g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
8652     g.NavMoveRequestFlags = move_flags;
8653     g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
8654 }
8655 
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)8656 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
8657 {
8658     ImGuiContext& g = *GImGui;
8659 
8660     // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
8661     // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
8662     g.NavWrapRequestWindow = window;
8663     g.NavWrapRequestFlags = move_flags;
8664 }
8665 
8666 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8667 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8668 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8669 {
8670     ImGuiWindow* parent = nav_window;
8671     while (parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8672         parent = parent->ParentWindow;
8673     if (parent && parent != nav_window)
8674         parent->NavLastChildNavWindow = nav_window;
8675 }
8676 
8677 // Restore the last focused child.
8678 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)8679 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8680 {
8681     if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
8682         return window->NavLastChildNavWindow;
8683     return window;
8684 }
8685 
NavRestoreLayer(ImGuiNavLayer layer)8686 static void NavRestoreLayer(ImGuiNavLayer layer)
8687 {
8688     ImGuiContext& g = *GImGui;
8689     g.NavLayer = layer;
8690     if (layer == 0)
8691         g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
8692     ImGuiWindow* window = g.NavWindow;
8693     if (layer == 0 && window->NavLastIds[0] != 0)
8694         ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]);
8695     else
8696         ImGui::NavInitWindow(window, true);
8697 }
8698 
NavUpdateAnyRequestFlag()8699 static inline void ImGui::NavUpdateAnyRequestFlag()
8700 {
8701     ImGuiContext& g = *GImGui;
8702     g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8703     if (g.NavAnyRequest)
8704         IM_ASSERT(g.NavWindow != NULL);
8705 }
8706 
8707 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)8708 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8709 {
8710     ImGuiContext& g = *GImGui;
8711     IM_ASSERT(window == g.NavWindow);
8712     bool init_for_nav = false;
8713     if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
8714         if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
8715             init_for_nav = true;
8716     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
8717     if (init_for_nav)
8718     {
8719         SetNavID(0, g.NavLayer, 0);
8720         g.NavInitRequest = true;
8721         g.NavInitRequestFromMove = false;
8722         g.NavInitResultId = 0;
8723         g.NavInitResultRectRel = ImRect();
8724         NavUpdateAnyRequestFlag();
8725     }
8726     else
8727     {
8728         g.NavId = window->NavLastIds[0];
8729         g.NavFocusScopeId = 0;
8730     }
8731 }
8732 
NavCalcPreferredRefPos()8733 static ImVec2 ImGui::NavCalcPreferredRefPos()
8734 {
8735     ImGuiContext& g = *GImGui;
8736     if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8737     {
8738         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8739         if (IsMousePosValid(&g.IO.MousePos))
8740             return g.IO.MousePos;
8741         return g.LastValidMousePos;
8742     }
8743     else
8744     {
8745         // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8746         const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8747         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()));
8748         ImRect visible_rect = GetViewportRect();
8749         return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max));   // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
8750     }
8751 }
8752 
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8753 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8754 {
8755     ImGuiContext& g = *GImGui;
8756     if (mode == ImGuiInputReadMode_Down)
8757         return g.IO.NavInputs[n];                         // Instant, read analog input (0.0f..1.0f, as provided by user)
8758 
8759     const float t = g.IO.NavInputsDownDuration[n];
8760     if (t < 0.0f && mode == ImGuiInputReadMode_Released)  // Return 1.0f when just released, no repeat, ignore analog input.
8761         return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8762     if (t < 0.0f)
8763         return 0.0f;
8764     if (mode == ImGuiInputReadMode_Pressed)               // Return 1.0f when just pressed, no repeat, ignore analog input.
8765         return (t == 0.0f) ? 1.0f : 0.0f;
8766     if (mode == ImGuiInputReadMode_Repeat)
8767         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8768     if (mode == ImGuiInputReadMode_RepeatSlow)
8769         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8770     if (mode == ImGuiInputReadMode_RepeatFast)
8771         return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8772     return 0.0f;
8773 }
8774 
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8775 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8776 {
8777     ImVec2 delta(0.0f, 0.0f);
8778     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8779         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
8780     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8781         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode)   - GetNavInputAmount(ImGuiNavInput_DpadLeft,   mode), GetNavInputAmount(ImGuiNavInput_DpadDown,   mode) - GetNavInputAmount(ImGuiNavInput_DpadUp,   mode));
8782     if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8783         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8784     if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8785         delta *= slow_factor;
8786     if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8787         delta *= fast_factor;
8788     return delta;
8789 }
8790 
NavUpdate()8791 static void ImGui::NavUpdate()
8792 {
8793     ImGuiContext& g = *GImGui;
8794     ImGuiIO& io = g.IO;
8795 
8796     io.WantSetMousePos = false;
8797     g.NavWrapRequestWindow = NULL;
8798     g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
8799 #if 0
8800     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);
8801 #endif
8802 
8803     // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
8804     // (do it before we map Keyboard input!)
8805     bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8806     bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8807     if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_NavGamepad)
8808     {
8809         if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f
8810             || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
8811             g.NavInputSource = ImGuiInputSource_NavGamepad;
8812     }
8813 
8814     // Update Keyboard->Nav inputs mapping
8815     if (nav_keyboard_active)
8816     {
8817         #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
8818         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
8819         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
8820         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
8821         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8822         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8823         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   );
8824         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8825         if (io.KeyCtrl)
8826             io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8827         if (io.KeyShift)
8828             io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8829         if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
8830             io.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f;
8831         #undef NAV_MAP_KEY
8832     }
8833     memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
8834     for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
8835         io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
8836 
8837     // Process navigation init request (select first/default focus)
8838     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
8839         NavUpdateInitResult();
8840     g.NavInitRequest = false;
8841     g.NavInitRequestFromMove = false;
8842     g.NavInitResultId = 0;
8843     g.NavJustMovedToId = 0;
8844 
8845     // Process navigation move request
8846     if (g.NavMoveRequest)
8847         NavUpdateMoveResult();
8848 
8849     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8850     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8851     {
8852         IM_ASSERT(g.NavMoveRequest);
8853         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8854             g.NavDisableHighlight = false;
8855         g.NavMoveRequestForward = ImGuiNavForward_None;
8856     }
8857 
8858     // Apply application mouse position movement, after we had a chance to process move request result.
8859     if (g.NavMousePosDirty && g.NavIdIsAlive)
8860     {
8861         // Set mouse position given our knowledge of the navigated item position from last frame
8862         if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8863         {
8864             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8865             {
8866                 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
8867                 io.WantSetMousePos = true;
8868             }
8869         }
8870         g.NavMousePosDirty = false;
8871     }
8872     g.NavIdIsAlive = false;
8873     g.NavJustTabbedId = 0;
8874     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8875 
8876     // 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
8877     if (g.NavWindow)
8878         NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8879     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
8880         g.NavWindow->NavLastChildNavWindow = NULL;
8881 
8882     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8883     NavUpdateWindowing();
8884 
8885     // Set output flags for user application
8886     io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8887     io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8888 
8889     // Process NavCancel input (to close a popup, get back to parent, clear focus)
8890     if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8891     {
8892         IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
8893         if (g.ActiveId != 0)
8894         {
8895             if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8896                 ClearActiveID();
8897         }
8898         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8899         {
8900             // Exit child window
8901             ImGuiWindow* child_window = g.NavWindow;
8902             ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8903             IM_ASSERT(child_window->ChildId != 0);
8904             FocusWindow(parent_window);
8905             SetNavID(child_window->ChildId, 0, 0);
8906             // Reassigning with same value, we're being explicit here.
8907             g.NavIdIsAlive = false;     // -V1048
8908             if (g.NavDisableMouseHover)
8909                 g.NavMousePosDirty = true;
8910         }
8911         else if (g.OpenPopupStack.Size > 0)
8912         {
8913             // Close open popup/menu
8914             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8915                 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8916         }
8917         else if (g.NavLayer != ImGuiNavLayer_Main)
8918         {
8919             // Leave the "menu" layer
8920             NavRestoreLayer(ImGuiNavLayer_Main);
8921         }
8922         else
8923         {
8924             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8925             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8926                 g.NavWindow->NavLastIds[0] = 0;
8927             g.NavId = g.NavFocusScopeId = 0;
8928         }
8929     }
8930 
8931     // Process manual activation request
8932     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8933     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8934     {
8935         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8936         bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8937         if (g.ActiveId == 0 && activate_pressed)
8938             g.NavActivateId = g.NavId;
8939         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8940             g.NavActivateDownId = g.NavId;
8941         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8942             g.NavActivatePressedId = g.NavId;
8943         if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8944             g.NavInputId = g.NavId;
8945     }
8946     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8947         g.NavDisableHighlight = true;
8948     if (g.NavActivateId != 0)
8949         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8950     g.NavMoveRequest = false;
8951 
8952     // Process programmatic activation request
8953     if (g.NavNextActivateId != 0)
8954         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8955     g.NavNextActivateId = 0;
8956 
8957     // Initiate directional inputs request
8958     if (g.NavMoveRequestForward == ImGuiNavForward_None)
8959     {
8960         g.NavMoveDir = ImGuiDir_None;
8961         g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8962         if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8963         {
8964             const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
8965             if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && (IsNavInputTest(ImGuiNavInput_DpadLeft,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_,  read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
8966             if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
8967             if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && (IsNavInputTest(ImGuiNavInput_DpadUp,    read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_,    read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
8968             if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && (IsNavInputTest(ImGuiNavInput_DpadDown,  read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_,  read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
8969         }
8970         g.NavMoveClipDir = g.NavMoveDir;
8971     }
8972     else
8973     {
8974         // 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)
8975         // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8976         IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8977         IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8978         IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
8979         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8980     }
8981 
8982     // Update PageUp/PageDown/Home/End scroll
8983     // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8984     float nav_scoring_rect_offset_y = 0.0f;
8985     if (nav_keyboard_active)
8986         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8987 
8988     // 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
8989     if (g.NavMoveDir != ImGuiDir_None)
8990     {
8991         g.NavMoveRequest = true;
8992         g.NavMoveRequestKeyMods = io.KeyMods;
8993         g.NavMoveDirLast = g.NavMoveDir;
8994     }
8995     if (g.NavMoveRequest && g.NavId == 0)
8996     {
8997         IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8998         g.NavInitRequest = g.NavInitRequestFromMove = true;
8999         // Reassigning with same value, we're being explicit here.
9000         g.NavInitResultId = 0;     // -V1048
9001         g.NavDisableHighlight = false;
9002     }
9003     NavUpdateAnyRequestFlag();
9004 
9005     // Scrolling
9006     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9007     {
9008         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9009         ImGuiWindow* window = g.NavWindow;
9010         const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
9011         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
9012         {
9013             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
9014                 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9015             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
9016                 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9017         }
9018 
9019         // *Normal* Manual scroll with NavScrollXXX keys
9020         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9021         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9022         if (scroll_dir.x != 0.0f && window->ScrollbarX)
9023             SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9024         if (scroll_dir.y != 0.0f)
9025             SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9026     }
9027 
9028     // Reset search results
9029     g.NavMoveResultLocal.Clear();
9030     g.NavMoveResultLocalVisibleSet.Clear();
9031     g.NavMoveResultOther.Clear();
9032 
9033     // When using gamepad, we project the reference nav bounding box into window visible area.
9034     // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
9035     // (can't focus a visible object like we can with the mouse).
9036     if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_NavGamepad && g.NavLayer == ImGuiNavLayer_Main)
9037     {
9038         ImGuiWindow* window = g.NavWindow;
9039         ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9040         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9041         {
9042             IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
9043             float pad = window->CalcFontSize() * 0.5f;
9044             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
9045             window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
9046             g.NavId = g.NavFocusScopeId = 0;
9047         }
9048     }
9049 
9050     // 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)
9051     ImRect nav_rect_rel = g.NavWindow ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9052     g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
9053     g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
9054     g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
9055     g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
9056     IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
9057     //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9058     g.NavScoringCount = 0;
9059 #if IMGUI_DEBUG_NAV_RECTS
9060     if (g.NavWindow)
9061     {
9062         ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9063         if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
9064         if (1) { 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); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
9065     }
9066 #endif
9067 }
9068 
NavUpdateInitResult()9069 static void ImGui::NavUpdateInitResult()
9070 {
9071     // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
9072     ImGuiContext& g = *GImGui;
9073     if (!g.NavWindow)
9074         return;
9075 
9076     // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9077     IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9078     if (g.NavInitRequestFromMove)
9079         SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
9080     else
9081         SetNavID(g.NavInitResultId, g.NavLayer, 0);
9082     g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
9083 }
9084 
9085 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()9086 static void ImGui::NavUpdateMoveResult()
9087 {
9088     ImGuiContext& g = *GImGui;
9089     if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9090     {
9091         // 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)
9092         if (g.NavId != 0)
9093         {
9094             g.NavDisableHighlight = false;
9095             g.NavDisableMouseHover = true;
9096         }
9097         return;
9098     }
9099 
9100     // Select which result to use
9101     ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9102 
9103     // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
9104     if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
9105         if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
9106             result = &g.NavMoveResultLocalVisibleSet;
9107 
9108     // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
9109     if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
9110         if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
9111             result = &g.NavMoveResultOther;
9112     IM_ASSERT(g.NavWindow && result->Window);
9113 
9114     // Scroll to keep newly navigated item fully into view.
9115     if (g.NavLayer == ImGuiNavLayer_Main)
9116     {
9117         ImVec2 delta_scroll;
9118         if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
9119         {
9120             float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
9121             delta_scroll.y = result->Window->Scroll.y - scroll_target;
9122             SetScrollY(result->Window, scroll_target);
9123         }
9124         else
9125         {
9126             ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
9127             delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
9128         }
9129 
9130         // Offset our result position so mouse position can be applied immediately after in NavUpdate()
9131         result->RectRel.TranslateX(-delta_scroll.x);
9132         result->RectRel.TranslateY(-delta_scroll.y);
9133     }
9134 
9135     ClearActiveID();
9136     g.NavWindow = result->Window;
9137     if (g.NavId != result->ID)
9138     {
9139         // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
9140         g.NavJustMovedToId = result->ID;
9141         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
9142         g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
9143     }
9144     IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
9145     SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
9146 }
9147 
9148 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()9149 static float ImGui::NavUpdatePageUpPageDown()
9150 {
9151     ImGuiContext& g = *GImGui;
9152     ImGuiIO& io = g.IO;
9153 
9154     if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
9155         return 0.0f;
9156     if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
9157         return 0.0f;
9158 
9159     ImGuiWindow* window = g.NavWindow;
9160     const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
9161     const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
9162     const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
9163     const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
9164     if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
9165     {
9166         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
9167         {
9168             // Fallback manual-scroll when window has no navigable item
9169             if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9170                 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
9171             else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9172                 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
9173             else if (home_pressed)
9174                 SetScrollY(window, 0.0f);
9175             else if (end_pressed)
9176                 SetScrollY(window, window->ScrollMax.y);
9177         }
9178         else
9179         {
9180             ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
9181             const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
9182             float nav_scoring_rect_offset_y = 0.0f;
9183             if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9184             {
9185                 nav_scoring_rect_offset_y = -page_offset_y;
9186                 g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
9187                 g.NavMoveClipDir = ImGuiDir_Up;
9188                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9189             }
9190             else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9191             {
9192                 nav_scoring_rect_offset_y = +page_offset_y;
9193                 g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
9194                 g.NavMoveClipDir = ImGuiDir_Down;
9195                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9196             }
9197             else if (home_pressed)
9198             {
9199                 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
9200                 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
9201                 // Preserve current horizontal position if we have any.
9202                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
9203                 if (nav_rect_rel.IsInverted())
9204                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9205                 g.NavMoveDir = ImGuiDir_Down;
9206                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9207             }
9208             else if (end_pressed)
9209             {
9210                 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
9211                 if (nav_rect_rel.IsInverted())
9212                     nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9213                 g.NavMoveDir = ImGuiDir_Up;
9214                 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9215             }
9216             return nav_scoring_rect_offset_y;
9217         }
9218     }
9219     return 0.0f;
9220 }
9221 
NavEndFrame()9222 static void ImGui::NavEndFrame()
9223 {
9224     ImGuiContext& g = *GImGui;
9225 
9226     // Show CTRL+TAB list window
9227     if (g.NavWindowingTarget != NULL)
9228         NavUpdateWindowingOverlay();
9229 
9230     // Perform wrap-around in menus
9231     ImGuiWindow* window = g.NavWrapRequestWindow;
9232     ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
9233     if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
9234     {
9235         IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
9236         ImRect bb_rel = window->NavRectRel[0];
9237 
9238         ImGuiDir clip_dir = g.NavMoveDir;
9239         if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9240         {
9241             bb_rel.Min.x = bb_rel.Max.x =
9242                 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
9243             if (move_flags & ImGuiNavMoveFlags_WrapX)
9244             {
9245                 bb_rel.TranslateY(-bb_rel.GetHeight());
9246                 clip_dir = ImGuiDir_Up;
9247             }
9248             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9249         }
9250         if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9251         {
9252             bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
9253             if (move_flags & ImGuiNavMoveFlags_WrapX)
9254             {
9255                 bb_rel.TranslateY(+bb_rel.GetHeight());
9256                 clip_dir = ImGuiDir_Down;
9257             }
9258             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9259         }
9260         if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9261         {
9262             bb_rel.Min.y = bb_rel.Max.y =
9263                 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
9264             if (move_flags & ImGuiNavMoveFlags_WrapY)
9265             {
9266                 bb_rel.TranslateX(-bb_rel.GetWidth());
9267                 clip_dir = ImGuiDir_Left;
9268             }
9269             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9270         }
9271         if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9272         {
9273             bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
9274             if (move_flags & ImGuiNavMoveFlags_WrapY)
9275             {
9276                 bb_rel.TranslateX(+bb_rel.GetWidth());
9277                 clip_dir = ImGuiDir_Right;
9278             }
9279             NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9280         }
9281     }
9282 }
9283 
FindWindowFocusIndex(ImGuiWindow * window)9284 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
9285 {
9286     ImGuiContext& g = *GImGui;
9287     for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
9288         if (g.WindowsFocusOrder[i] == window)
9289             return i;
9290     return -1;
9291 }
9292 
FindWindowNavFocusable(int i_start,int i_stop,int dir)9293 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
9294 {
9295     ImGuiContext& g = *GImGui;
9296     for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
9297         if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
9298             return g.WindowsFocusOrder[i];
9299     return NULL;
9300 }
9301 
NavUpdateWindowingHighlightWindow(int focus_change_dir)9302 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
9303 {
9304     ImGuiContext& g = *GImGui;
9305     IM_ASSERT(g.NavWindowingTarget);
9306     if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
9307         return;
9308 
9309     const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
9310     ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
9311     if (!window_target)
9312         window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
9313     if (window_target) // Don't reset windowing target if there's a single window in the list
9314         g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
9315     g.NavWindowingToggleLayer = false;
9316 }
9317 
9318 // Windowing management mode
9319 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
9320 // Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()9321 static void ImGui::NavUpdateWindowing()
9322 {
9323     ImGuiContext& g = *GImGui;
9324     ImGuiWindow* apply_focus_window = NULL;
9325     bool apply_toggle_layer = false;
9326 
9327     ImGuiWindow* modal_window = GetTopMostPopupModal();
9328     bool allow_windowing = (modal_window == NULL);
9329     if (!allow_windowing)
9330         g.NavWindowingTarget = NULL;
9331 
9332     // Fade out
9333     if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
9334     {
9335         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
9336         if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
9337             g.NavWindowingTargetAnim = NULL;
9338     }
9339 
9340     // Start CTRL-TAB or Square+L/R window selection
9341     bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
9342     bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
9343     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
9344         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
9345         {
9346             g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop
9347             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
9348             g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
9349             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
9350         }
9351 
9352     // Gamepad update
9353     g.NavWindowingTimer += g.IO.DeltaTime;
9354     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
9355     {
9356         // 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
9357         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
9358 
9359         // Select window to focus
9360         const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
9361         if (focus_change_dir != 0)
9362         {
9363             NavUpdateWindowingHighlightWindow(focus_change_dir);
9364             g.NavWindowingHighlightAlpha = 1.0f;
9365         }
9366 
9367         // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
9368         if (!IsNavInputDown(ImGuiNavInput_Menu))
9369         {
9370             g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
9371             if (g.NavWindowingToggleLayer && g.NavWindow)
9372                 apply_toggle_layer = true;
9373             else if (!g.NavWindowingToggleLayer)
9374                 apply_focus_window = g.NavWindowingTarget;
9375             g.NavWindowingTarget = NULL;
9376         }
9377     }
9378 
9379     // Keyboard: Focus
9380     if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
9381     {
9382         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
9383         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
9384         if (IsKeyPressedMap(ImGuiKey_Tab, true))
9385             NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
9386         if (!g.IO.KeyCtrl)
9387             apply_focus_window = g.NavWindowingTarget;
9388     }
9389 
9390     // Keyboard: Press and Release ALT to toggle menu layer
9391     // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB
9392     if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
9393         g.NavWindowingToggleLayer = true;
9394     if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
9395         if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
9396             apply_toggle_layer = true;
9397 
9398     // Move window
9399     if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
9400     {
9401         ImVec2 move_delta;
9402         if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
9403             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
9404         if (g.NavInputSource == ImGuiInputSource_NavGamepad)
9405             move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
9406         if (move_delta.x != 0.0f || move_delta.y != 0.0f)
9407         {
9408             const float NAV_MOVE_SPEED = 800.0f;
9409             const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
9410             ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
9411             SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
9412             MarkIniSettingsDirty(moving_window);
9413             g.NavDisableMouseHover = true;
9414         }
9415     }
9416 
9417     // Apply final focus
9418     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
9419     {
9420         ClearActiveID();
9421         g.NavDisableHighlight = false;
9422         g.NavDisableMouseHover = true;
9423         apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
9424         ClosePopupsOverWindow(apply_focus_window, false);
9425         FocusWindow(apply_focus_window);
9426         if (apply_focus_window->NavLastIds[0] == 0)
9427             NavInitWindow(apply_focus_window, false);
9428 
9429         // If the window only has a menu layer, select it directly
9430         if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
9431             g.NavLayer = ImGuiNavLayer_Menu;
9432     }
9433     if (apply_focus_window)
9434         g.NavWindowingTarget = NULL;
9435 
9436     // Apply menu/layer toggle
9437     if (apply_toggle_layer && g.NavWindow)
9438     {
9439         // Move to parent menu if necessary
9440         ImGuiWindow* new_nav_window = g.NavWindow;
9441         while (new_nav_window->ParentWindow
9442             && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
9443             && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
9444             && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9445             new_nav_window = new_nav_window->ParentWindow;
9446         if (new_nav_window != g.NavWindow)
9447         {
9448             ImGuiWindow* old_nav_window = g.NavWindow;
9449             FocusWindow(new_nav_window);
9450             new_nav_window->NavLastChildNavWindow = old_nav_window;
9451         }
9452         g.NavDisableHighlight = false;
9453         g.NavDisableMouseHover = true;
9454 
9455         // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
9456         const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
9457         NavRestoreLayer(new_nav_layer);
9458     }
9459 }
9460 
9461 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)9462 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
9463 {
9464     if (window->Flags & ImGuiWindowFlags_Popup)
9465         return "(Popup)";
9466     if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
9467         return "(Main menu bar)";
9468     return "(Untitled)";
9469 }
9470 
9471 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()9472 void ImGui::NavUpdateWindowingOverlay()
9473 {
9474     ImGuiContext& g = *GImGui;
9475     IM_ASSERT(g.NavWindowingTarget != NULL);
9476 
9477     if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
9478         return;
9479 
9480     if (g.NavWindowingListWindow == NULL)
9481         g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
9482     SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
9483     SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9484     PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
9485     Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
9486     for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
9487     {
9488         ImGuiWindow* window = g.WindowsFocusOrder[n];
9489         if (!IsWindowNavFocusable(window))
9490             continue;
9491         const char* label = window->Name;
9492         if (label == FindRenderedTextEnd(label))
9493             label = GetFallbackWindowNameForWindowingList(window);
9494         Selectable(label, g.NavWindowingTarget == window);
9495     }
9496     End();
9497     PopStyleVar();
9498 }
9499 
9500 
9501 //-----------------------------------------------------------------------------
9502 // [SECTION] DRAG AND DROP
9503 //-----------------------------------------------------------------------------
9504 
ClearDragDrop()9505 void ImGui::ClearDragDrop()
9506 {
9507     ImGuiContext& g = *GImGui;
9508     g.DragDropActive = false;
9509     g.DragDropPayload.Clear();
9510     g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
9511     g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
9512     g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
9513     g.DragDropAcceptFrameCount = -1;
9514 
9515     g.DragDropPayloadBufHeap.clear();
9516     memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9517 }
9518 
9519 // Call when current ID is active.
9520 // 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)9521 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
9522 {
9523     ImGuiContext& g = *GImGui;
9524     ImGuiWindow* window = g.CurrentWindow;
9525 
9526     bool source_drag_active = false;
9527     ImGuiID source_id = 0;
9528     ImGuiID source_parent_id = 0;
9529     ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
9530     if (!(flags & ImGuiDragDropFlags_SourceExtern))
9531     {
9532         source_id = window->DC.LastItemId;
9533         if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
9534             return false;
9535         if (g.IO.MouseDown[mouse_button] == false)
9536             return false;
9537 
9538         if (source_id == 0)
9539         {
9540             // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
9541             // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
9542             if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
9543             {
9544                 IM_ASSERT(0);
9545                 return false;
9546             }
9547 
9548             // Early out
9549             if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
9550                 return false;
9551 
9552             // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
9553             // We build a throwaway ID based on current ID stack + relative AABB of items in window.
9554             // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
9555             // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
9556             source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
9557             bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
9558             if (is_hovered && g.IO.MouseClicked[mouse_button])
9559             {
9560                 SetActiveID(source_id, window);
9561                 FocusWindow(window);
9562             }
9563             if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
9564                 g.ActiveIdAllowOverlap = is_hovered;
9565         }
9566         else
9567         {
9568             g.ActiveIdAllowOverlap = false;
9569         }
9570         if (g.ActiveId != source_id)
9571             return false;
9572         source_parent_id = window->IDStack.back();
9573         source_drag_active = IsMouseDragging(mouse_button);
9574 
9575         // Disable navigation and key inputs while dragging
9576         g.ActiveIdUsingNavDirMask = ~(ImU32)0;
9577         g.ActiveIdUsingNavInputMask = ~(ImU32)0;
9578         g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
9579     }
9580     else
9581     {
9582         window = NULL;
9583         source_id = ImHashStr("#SourceExtern");
9584         source_drag_active = true;
9585     }
9586 
9587     if (source_drag_active)
9588     {
9589         if (!g.DragDropActive)
9590         {
9591             IM_ASSERT(source_id != 0);
9592             ClearDragDrop();
9593             ImGuiPayload& payload = g.DragDropPayload;
9594             payload.SourceId = source_id;
9595             payload.SourceParentId = source_parent_id;
9596             g.DragDropActive = true;
9597             g.DragDropSourceFlags = flags;
9598             g.DragDropMouseButton = mouse_button;
9599             if (payload.SourceId == g.ActiveId)
9600                 g.ActiveIdNoClearOnFocusLoss = true;
9601         }
9602         g.DragDropSourceFrameCount = g.FrameCount;
9603         g.DragDropWithinSource = true;
9604 
9605         if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9606         {
9607             // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
9608             // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
9609             BeginTooltip();
9610             if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
9611             {
9612                 ImGuiWindow* tooltip_window = g.CurrentWindow;
9613                 tooltip_window->SkipItems = true;
9614                 tooltip_window->HiddenFramesCanSkipItems = 1;
9615             }
9616         }
9617 
9618         if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
9619             window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
9620 
9621         return true;
9622     }
9623     return false;
9624 }
9625 
EndDragDropSource()9626 void ImGui::EndDragDropSource()
9627 {
9628     ImGuiContext& g = *GImGui;
9629     IM_ASSERT(g.DragDropActive);
9630     IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
9631 
9632     if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9633         EndTooltip();
9634 
9635     // Discard the drag if have not called SetDragDropPayload()
9636     if (g.DragDropPayload.DataFrameCount == -1)
9637         ClearDragDrop();
9638     g.DragDropWithinSource = false;
9639 }
9640 
9641 // 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)9642 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
9643 {
9644     ImGuiContext& g = *GImGui;
9645     ImGuiPayload& payload = g.DragDropPayload;
9646     if (cond == 0)
9647         cond = ImGuiCond_Always;
9648 
9649     IM_ASSERT(type != NULL);
9650     IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
9651     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
9652     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
9653     IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()
9654 
9655     if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
9656     {
9657         // Copy payload
9658         ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
9659         g.DragDropPayloadBufHeap.resize(0);
9660         if (data_size > sizeof(g.DragDropPayloadBufLocal))
9661         {
9662             // Store in heap
9663             g.DragDropPayloadBufHeap.resize((int)data_size);
9664             payload.Data = g.DragDropPayloadBufHeap.Data;
9665             memcpy(payload.Data, data, data_size);
9666         }
9667         else if (data_size > 0)
9668         {
9669             // Store locally
9670             memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9671             payload.Data = g.DragDropPayloadBufLocal;
9672             memcpy(payload.Data, data, data_size);
9673         }
9674         else
9675         {
9676             payload.Data = NULL;
9677         }
9678         payload.DataSize = (int)data_size;
9679     }
9680     payload.DataFrameCount = g.FrameCount;
9681 
9682     return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
9683 }
9684 
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)9685 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
9686 {
9687     ImGuiContext& g = *GImGui;
9688     if (!g.DragDropActive)
9689         return false;
9690 
9691     ImGuiWindow* window = g.CurrentWindow;
9692     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
9693     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
9694         return false;
9695     IM_ASSERT(id != 0);
9696     if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
9697         return false;
9698     if (window->SkipItems)
9699         return false;
9700 
9701     IM_ASSERT(g.DragDropWithinTarget == false);
9702     g.DragDropTargetRect = bb;
9703     g.DragDropTargetId = id;
9704     g.DragDropWithinTarget = true;
9705     return true;
9706 }
9707 
9708 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
9709 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
9710 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
9711 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()9712 bool ImGui::BeginDragDropTarget()
9713 {
9714     ImGuiContext& g = *GImGui;
9715     if (!g.DragDropActive)
9716         return false;
9717 
9718     ImGuiWindow* window = g.CurrentWindow;
9719     if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
9720         return false;
9721     ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
9722     if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
9723         return false;
9724 
9725     const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
9726     ImGuiID id = window->DC.LastItemId;
9727     if (id == 0)
9728         id = window->GetIDFromRectangle(display_rect);
9729     if (g.DragDropPayload.SourceId == id)
9730         return false;
9731 
9732     IM_ASSERT(g.DragDropWithinTarget == false);
9733     g.DragDropTargetRect = display_rect;
9734     g.DragDropTargetId = id;
9735     g.DragDropWithinTarget = true;
9736     return true;
9737 }
9738 
IsDragDropPayloadBeingAccepted()9739 bool ImGui::IsDragDropPayloadBeingAccepted()
9740 {
9741     ImGuiContext& g = *GImGui;
9742     return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
9743 }
9744 
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)9745 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
9746 {
9747     ImGuiContext& g = *GImGui;
9748     ImGuiWindow* window = g.CurrentWindow;
9749     ImGuiPayload& payload = g.DragDropPayload;
9750     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
9751     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
9752     if (type != NULL && !payload.IsDataType(type))
9753         return NULL;
9754 
9755     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
9756     // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
9757     const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
9758     ImRect r = g.DragDropTargetRect;
9759     float r_surface = r.GetWidth() * r.GetHeight();
9760     if (r_surface <= g.DragDropAcceptIdCurrRectSurface)
9761     {
9762         g.DragDropAcceptFlags = flags;
9763         g.DragDropAcceptIdCurr = g.DragDropTargetId;
9764         g.DragDropAcceptIdCurrRectSurface = r_surface;
9765     }
9766 
9767     // Render default drop visuals
9768     payload.Preview = was_accepted_previously;
9769     flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
9770     if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
9771     {
9772         // FIXME-DRAG: Settle on a proper default visuals for drop target.
9773         r.Expand(3.5f);
9774         bool push_clip_rect = !window->ClipRect.Contains(r);
9775         if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1));
9776         window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
9777         if (push_clip_rect) window->DrawList->PopClipRect();
9778     }
9779 
9780     g.DragDropAcceptFrameCount = g.FrameCount;
9781     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()
9782     if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
9783         return NULL;
9784 
9785     return &payload;
9786 }
9787 
GetDragDropPayload()9788 const ImGuiPayload* ImGui::GetDragDropPayload()
9789 {
9790     ImGuiContext& g = *GImGui;
9791     return g.DragDropActive ? &g.DragDropPayload : NULL;
9792 }
9793 
9794 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()9795 void ImGui::EndDragDropTarget()
9796 {
9797     ImGuiContext& g = *GImGui;
9798     IM_ASSERT(g.DragDropActive);
9799     IM_ASSERT(g.DragDropWithinTarget);
9800     g.DragDropWithinTarget = false;
9801 }
9802 
9803 //-----------------------------------------------------------------------------
9804 // [SECTION] LOGGING/CAPTURING
9805 //-----------------------------------------------------------------------------
9806 // All text output from the interface can be captured into tty/file/clipboard.
9807 // By default, tree nodes are automatically opened during logging.
9808 //-----------------------------------------------------------------------------
9809 
9810 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)9811 void ImGui::LogText(const char* fmt, ...)
9812 {
9813     ImGuiContext& g = *GImGui;
9814     if (!g.LogEnabled)
9815         return;
9816 
9817     va_list args;
9818     va_start(args, fmt);
9819     if (g.LogFile)
9820     {
9821         g.LogBuffer.Buf.resize(0);
9822         g.LogBuffer.appendfv(fmt, args);
9823         ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
9824     }
9825     else
9826     {
9827         g.LogBuffer.appendfv(fmt, args);
9828     }
9829     va_end(args);
9830 }
9831 
9832 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9833 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9834 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9835 {
9836     ImGuiContext& g = *GImGui;
9837     ImGuiWindow* window = g.CurrentWindow;
9838 
9839     if (!text_end)
9840         text_end = FindRenderedTextEnd(text, text_end);
9841 
9842     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9843     if (ref_pos)
9844         g.LogLinePosY = ref_pos->y;
9845     if (log_new_line)
9846         g.LogLineFirstItem = true;
9847 
9848     const char* text_remaining = text;
9849     if (g.LogDepthRef > window->DC.TreeDepth)  // Re-adjust padding if we have popped out of our starting depth
9850         g.LogDepthRef = window->DC.TreeDepth;
9851     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9852     for (;;)
9853     {
9854         // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9855         // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9856         const char* line_start = text_remaining;
9857         const char* line_end = ImStreolRange(line_start, text_end);
9858         const bool is_first_line = (line_start == text);
9859         const bool is_last_line = (line_end == text_end);
9860         if (!is_last_line || (line_start != line_end))
9861         {
9862             const int char_count = (int)(line_end - line_start);
9863             if (log_new_line || !is_first_line)
9864                 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9865             else if (g.LogLineFirstItem)
9866                 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9867             else
9868                 LogText(" %.*s", char_count, line_start);
9869             g.LogLineFirstItem = false;
9870         }
9871         else if (log_new_line)
9872         {
9873             // An empty "" string at a different Y position should output a carriage return.
9874             LogText(IM_NEWLINE);
9875             break;
9876         }
9877 
9878         if (is_last_line)
9879             break;
9880         text_remaining = line_end + 1;
9881     }
9882 }
9883 
9884 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9885 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9886 {
9887     ImGuiContext& g = *GImGui;
9888     ImGuiWindow* window = g.CurrentWindow;
9889     IM_ASSERT(g.LogEnabled == false);
9890     IM_ASSERT(g.LogFile == NULL);
9891     IM_ASSERT(g.LogBuffer.empty());
9892     g.LogEnabled = true;
9893     g.LogType = type;
9894     g.LogDepthRef = window->DC.TreeDepth;
9895     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9896     g.LogLinePosY = FLT_MAX;
9897     g.LogLineFirstItem = true;
9898 }
9899 
LogToTTY(int auto_open_depth)9900 void ImGui::LogToTTY(int auto_open_depth)
9901 {
9902     ImGuiContext& g = *GImGui;
9903     if (g.LogEnabled)
9904         return;
9905     IM_UNUSED(auto_open_depth);
9906 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9907     LogBegin(ImGuiLogType_TTY, auto_open_depth);
9908     g.LogFile = stdout;
9909 #endif
9910 }
9911 
9912 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9913 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9914 {
9915     ImGuiContext& g = *GImGui;
9916     if (g.LogEnabled)
9917         return;
9918 
9919     // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9920     // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9921     // By opening the file in binary mode "ab" we have consistent output everywhere.
9922     if (!filename)
9923         filename = g.IO.LogFilename;
9924     if (!filename || !filename[0])
9925         return;
9926     ImFileHandle f = ImFileOpen(filename, "ab");
9927     if (!f)
9928     {
9929         IM_ASSERT(0);
9930         return;
9931     }
9932 
9933     LogBegin(ImGuiLogType_File, auto_open_depth);
9934     g.LogFile = f;
9935 }
9936 
9937 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9938 void ImGui::LogToClipboard(int auto_open_depth)
9939 {
9940     ImGuiContext& g = *GImGui;
9941     if (g.LogEnabled)
9942         return;
9943     LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9944 }
9945 
LogToBuffer(int auto_open_depth)9946 void ImGui::LogToBuffer(int auto_open_depth)
9947 {
9948     ImGuiContext& g = *GImGui;
9949     if (g.LogEnabled)
9950         return;
9951     LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9952 }
9953 
LogFinish()9954 void ImGui::LogFinish()
9955 {
9956     ImGuiContext& g = *GImGui;
9957     if (!g.LogEnabled)
9958         return;
9959 
9960     LogText(IM_NEWLINE);
9961     switch (g.LogType)
9962     {
9963     case ImGuiLogType_TTY:
9964 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9965         fflush(g.LogFile);
9966 #endif
9967         break;
9968     case ImGuiLogType_File:
9969         ImFileClose(g.LogFile);
9970         break;
9971     case ImGuiLogType_Buffer:
9972         break;
9973     case ImGuiLogType_Clipboard:
9974         if (!g.LogBuffer.empty())
9975             SetClipboardText(g.LogBuffer.begin());
9976         break;
9977     case ImGuiLogType_None:
9978         IM_ASSERT(0);
9979         break;
9980     }
9981 
9982     g.LogEnabled = false;
9983     g.LogType = ImGuiLogType_None;
9984     g.LogFile = NULL;
9985     g.LogBuffer.clear();
9986 }
9987 
9988 // Helper to display logging buttons
9989 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9990 void ImGui::LogButtons()
9991 {
9992     ImGuiContext& g = *GImGui;
9993 
9994     PushID("LogButtons");
9995 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9996     const bool log_to_tty = Button("Log To TTY"); SameLine();
9997 #else
9998     const bool log_to_tty = false;
9999 #endif
10000     const bool log_to_file = Button("Log To File"); SameLine();
10001     const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10002     PushAllowKeyboardFocus(false);
10003     SetNextItemWidth(80.0f);
10004     SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10005     PopAllowKeyboardFocus();
10006     PopID();
10007 
10008     // Start logging at the end of the function so that the buttons don't appear in the log
10009     if (log_to_tty)
10010         LogToTTY();
10011     if (log_to_file)
10012         LogToFile();
10013     if (log_to_clipboard)
10014         LogToClipboard();
10015 }
10016 
10017 
10018 //-----------------------------------------------------------------------------
10019 // [SECTION] SETTINGS
10020 //-----------------------------------------------------------------------------
10021 // - UpdateSettings() [Internal]
10022 // - MarkIniSettingsDirty() [Internal]
10023 // - CreateNewWindowSettings() [Internal]
10024 // - FindWindowSettings() [Internal]
10025 // - FindOrCreateWindowSettings() [Internal]
10026 // - FindSettingsHandler() [Internal]
10027 // - ClearIniSettings() [Internal]
10028 // - LoadIniSettingsFromDisk()
10029 // - LoadIniSettingsFromMemory()
10030 // - SaveIniSettingsToDisk()
10031 // - SaveIniSettingsToMemory()
10032 // - WindowSettingsHandler_***() [Internal]
10033 //-----------------------------------------------------------------------------
10034 
10035 // Called by NewFrame()
UpdateSettings()10036 void ImGui::UpdateSettings()
10037 {
10038     // Load settings on first frame (if not explicitly loaded manually before)
10039     ImGuiContext& g = *GImGui;
10040     if (!g.SettingsLoaded)
10041     {
10042         IM_ASSERT(g.SettingsWindows.empty());
10043         if (g.IO.IniFilename)
10044             LoadIniSettingsFromDisk(g.IO.IniFilename);
10045         g.SettingsLoaded = true;
10046     }
10047 
10048     // Save settings (with a delay after the last modification, so we don't spam disk too much)
10049     if (g.SettingsDirtyTimer > 0.0f)
10050     {
10051         g.SettingsDirtyTimer -= g.IO.DeltaTime;
10052         if (g.SettingsDirtyTimer <= 0.0f)
10053         {
10054             if (g.IO.IniFilename != NULL)
10055                 SaveIniSettingsToDisk(g.IO.IniFilename);
10056             else
10057                 g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
10058             g.SettingsDirtyTimer = 0.0f;
10059         }
10060     }
10061 }
10062 
MarkIniSettingsDirty()10063 void ImGui::MarkIniSettingsDirty()
10064 {
10065     ImGuiContext& g = *GImGui;
10066     if (g.SettingsDirtyTimer <= 0.0f)
10067         g.SettingsDirtyTimer = g.IO.IniSavingRate;
10068 }
10069 
MarkIniSettingsDirty(ImGuiWindow * window)10070 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
10071 {
10072     ImGuiContext& g = *GImGui;
10073     if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
10074         if (g.SettingsDirtyTimer <= 0.0f)
10075             g.SettingsDirtyTimer = g.IO.IniSavingRate;
10076 }
10077 
CreateNewWindowSettings(const char * name)10078 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
10079 {
10080     ImGuiContext& g = *GImGui;
10081 
10082 #if !IMGUI_DEBUG_INI_SETTINGS
10083     // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
10084     // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
10085     if (const char* p = strstr(name, "###"))
10086         name = p;
10087 #endif
10088     const size_t name_len = strlen(name);
10089 
10090     // Allocate chunk
10091     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
10092     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
10093     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
10094     settings->ID = ImHashStr(name, name_len);
10095     memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
10096 
10097     return settings;
10098 }
10099 
FindWindowSettings(ImGuiID id)10100 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
10101 {
10102     ImGuiContext& g = *GImGui;
10103     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10104         if (settings->ID == id)
10105             return settings;
10106     return NULL;
10107 }
10108 
FindOrCreateWindowSettings(const char * name)10109 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
10110 {
10111     if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
10112         return settings;
10113     return CreateNewWindowSettings(name);
10114 }
10115 
FindSettingsHandler(const char * type_name)10116 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
10117 {
10118     ImGuiContext& g = *GImGui;
10119     const ImGuiID type_hash = ImHashStr(type_name);
10120     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10121         if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
10122             return &g.SettingsHandlers[handler_n];
10123     return NULL;
10124 }
10125 
ClearIniSettings()10126 void ImGui::ClearIniSettings()
10127 {
10128     ImGuiContext& g = *GImGui;
10129     g.SettingsIniData.clear();
10130     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10131         if (g.SettingsHandlers[handler_n].ClearAllFn)
10132             g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
10133 }
10134 
LoadIniSettingsFromDisk(const char * ini_filename)10135 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
10136 {
10137     size_t file_data_size = 0;
10138     char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
10139     if (!file_data)
10140         return;
10141     LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
10142     IM_FREE(file_data);
10143 }
10144 
10145 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)10146 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
10147 {
10148     ImGuiContext& g = *GImGui;
10149     IM_ASSERT(g.Initialized);
10150     //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
10151     //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
10152 
10153     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
10154     // 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..
10155     if (ini_size == 0)
10156         ini_size = strlen(ini_data);
10157     g.SettingsIniData.Buf.resize((int)ini_size + 1);
10158     char* const buf = g.SettingsIniData.Buf.Data;
10159     char* const buf_end = buf + ini_size;
10160     memcpy(buf, ini_data, ini_size);
10161     buf_end[0] = 0;
10162 
10163     // Call pre-read handlers
10164     // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
10165     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10166         if (g.SettingsHandlers[handler_n].ReadInitFn)
10167             g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
10168 
10169     void* entry_data = NULL;
10170     ImGuiSettingsHandler* entry_handler = NULL;
10171 
10172     char* line_end = NULL;
10173     for (char* line = buf; line < buf_end; line = line_end + 1)
10174     {
10175         // Skip new lines markers, then find end of the line
10176         while (*line == '\n' || *line == '\r')
10177             line++;
10178         line_end = line;
10179         while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
10180             line_end++;
10181         line_end[0] = 0;
10182         if (line[0] == ';')
10183             continue;
10184         if (line[0] == '[' && line_end > line && line_end[-1] == ']')
10185         {
10186             // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
10187             line_end[-1] = 0;
10188             const char* name_end = line_end - 1;
10189             const char* type_start = line + 1;
10190             char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
10191             const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
10192             if (!type_end || !name_start)
10193                 continue;
10194             *type_end = 0; // Overwrite first ']'
10195             name_start++;  // Skip second '['
10196             entry_handler = FindSettingsHandler(type_start);
10197             entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
10198         }
10199         else if (entry_handler != NULL && entry_data != NULL)
10200         {
10201             // Let type handler parse the line
10202             entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
10203         }
10204     }
10205     g.SettingsLoaded = true;
10206 
10207     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
10208     memcpy(buf, ini_data, ini_size);
10209 
10210     // Call post-read handlers
10211     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10212         if (g.SettingsHandlers[handler_n].ApplyAllFn)
10213             g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
10214 }
10215 
SaveIniSettingsToDisk(const char * ini_filename)10216 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
10217 {
10218     ImGuiContext& g = *GImGui;
10219     g.SettingsDirtyTimer = 0.0f;
10220     if (!ini_filename)
10221         return;
10222 
10223     size_t ini_data_size = 0;
10224     const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
10225     ImFileHandle f = ImFileOpen(ini_filename, "wt");
10226     if (!f)
10227         return;
10228     ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
10229     ImFileClose(f);
10230 }
10231 
10232 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)10233 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
10234 {
10235     ImGuiContext& g = *GImGui;
10236     g.SettingsDirtyTimer = 0.0f;
10237     g.SettingsIniData.Buf.resize(0);
10238     g.SettingsIniData.Buf.push_back(0);
10239     for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10240     {
10241         ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
10242         handler->WriteAllFn(&g, handler, &g.SettingsIniData);
10243     }
10244     if (out_size)
10245         *out_size = (size_t)g.SettingsIniData.size();
10246     return g.SettingsIniData.c_str();
10247 }
10248 
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10249 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10250 {
10251     ImGuiContext& g = *ctx;
10252     for (int i = 0; i != g.Windows.Size; i++)
10253         g.Windows[i]->SettingsOffset = -1;
10254     g.SettingsWindows.clear();
10255 }
10256 
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)10257 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
10258 {
10259     ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
10260     ImGuiID id = settings->ID;
10261     *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
10262     settings->ID = id;
10263     settings->WantApply = true;
10264     return (void*)settings;
10265 }
10266 
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)10267 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
10268 {
10269     ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
10270     int x, y;
10271     int i;
10272     if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)         { settings->Pos = ImVec2ih((short)x, (short)y); }
10273     else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)   { settings->Size = ImVec2ih((short)x, (short)y); }
10274     else if (sscanf(line, "Collapsed=%d", &i) == 1)     { settings->Collapsed = (i != 0); }
10275 }
10276 
10277 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10278 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10279 {
10280     ImGuiContext& g = *ctx;
10281     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10282         if (settings->WantApply)
10283         {
10284             if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
10285                 ApplyWindowSettings(window, settings);
10286             settings->WantApply = false;
10287         }
10288 }
10289 
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)10290 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
10291 {
10292     // Gather data from windows that were active during this session
10293     // (if a window wasn't opened in this session we preserve its settings)
10294     ImGuiContext& g = *ctx;
10295     for (int i = 0; i != g.Windows.Size; i++)
10296     {
10297         ImGuiWindow* window = g.Windows[i];
10298         if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
10299             continue;
10300 
10301         ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
10302         if (!settings)
10303         {
10304             settings = ImGui::CreateNewWindowSettings(window->Name);
10305             window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
10306         }
10307         IM_ASSERT(settings->ID == window->ID);
10308         settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
10309         settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
10310         settings->Collapsed = window->Collapsed;
10311     }
10312 
10313     // Write to text buffer
10314     buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
10315     for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10316     {
10317         const char* settings_name = settings->GetName();
10318         buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
10319         buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
10320         buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
10321         buf->appendf("Collapsed=%d\n", settings->Collapsed);
10322         buf->append("\n");
10323     }
10324 }
10325 
10326 
10327 //-----------------------------------------------------------------------------
10328 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
10329 //-----------------------------------------------------------------------------
10330 
10331 // (this section is filled in the 'docking' branch)
10332 
10333 
10334 //-----------------------------------------------------------------------------
10335 // [SECTION] DOCKING
10336 //-----------------------------------------------------------------------------
10337 
10338 // (this section is filled in the 'docking' branch)
10339 
10340 
10341 //-----------------------------------------------------------------------------
10342 // [SECTION] PLATFORM DEPENDENT HELPERS
10343 //-----------------------------------------------------------------------------
10344 
10345 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10346 
10347 #ifdef _MSC_VER
10348 #pragma comment(lib, "user32")
10349 #pragma comment(lib, "kernel32")
10350 #endif
10351 
10352 // Win32 clipboard implementation
10353 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)10354 static const char* GetClipboardTextFn_DefaultImpl(void*)
10355 {
10356     ImGuiContext& g = *GImGui;
10357     g.ClipboardHandlerData.clear();
10358     if (!::OpenClipboard(NULL))
10359         return NULL;
10360     HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
10361     if (wbuf_handle == NULL)
10362     {
10363         ::CloseClipboard();
10364         return NULL;
10365     }
10366     if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
10367     {
10368         int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
10369         g.ClipboardHandlerData.resize(buf_len);
10370         ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
10371     }
10372     ::GlobalUnlock(wbuf_handle);
10373     ::CloseClipboard();
10374     return g.ClipboardHandlerData.Data;
10375 }
10376 
SetClipboardTextFn_DefaultImpl(void *,const char * text)10377 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10378 {
10379     if (!::OpenClipboard(NULL))
10380         return;
10381     const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
10382     HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
10383     if (wbuf_handle == NULL)
10384     {
10385         ::CloseClipboard();
10386         return;
10387     }
10388     WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
10389     ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
10390     ::GlobalUnlock(wbuf_handle);
10391     ::EmptyClipboard();
10392     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
10393         ::GlobalFree(wbuf_handle);
10394     ::CloseClipboard();
10395 }
10396 
10397 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
10398 
10399 #include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
10400 static PasteboardRef main_clipboard = 0;
10401 
10402 // OSX clipboard implementation
10403 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)10404 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10405 {
10406     if (!main_clipboard)
10407         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10408     PasteboardClear(main_clipboard);
10409     CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
10410     if (cf_data)
10411     {
10412         PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
10413         CFRelease(cf_data);
10414     }
10415 }
10416 
GetClipboardTextFn_DefaultImpl(void *)10417 static const char* GetClipboardTextFn_DefaultImpl(void*)
10418 {
10419     if (!main_clipboard)
10420         PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10421     PasteboardSynchronize(main_clipboard);
10422 
10423     ItemCount item_count = 0;
10424     PasteboardGetItemCount(main_clipboard, &item_count);
10425     for (ItemCount i = 0; i < item_count; i++)
10426     {
10427         PasteboardItemID item_id = 0;
10428         PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
10429         CFArrayRef flavor_type_array = 0;
10430         PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
10431         for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
10432         {
10433             CFDataRef cf_data;
10434             if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
10435             {
10436                 ImGuiContext& g = *GImGui;
10437                 g.ClipboardHandlerData.clear();
10438                 int length = (int)CFDataGetLength(cf_data);
10439                 g.ClipboardHandlerData.resize(length + 1);
10440                 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
10441                 g.ClipboardHandlerData[length] = 0;
10442                 CFRelease(cf_data);
10443                 return g.ClipboardHandlerData.Data;
10444             }
10445         }
10446     }
10447     return NULL;
10448 }
10449 
10450 #else
10451 
10452 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)10453 static const char* GetClipboardTextFn_DefaultImpl(void*)
10454 {
10455     ImGuiContext& g = *GImGui;
10456     return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
10457 }
10458 
SetClipboardTextFn_DefaultImpl(void *,const char * text)10459 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10460 {
10461     ImGuiContext& g = *GImGui;
10462     g.ClipboardHandlerData.clear();
10463     const char* text_end = text + strlen(text);
10464     g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
10465     memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
10466     g.ClipboardHandlerData[(int)(text_end - text)] = 0;
10467 }
10468 
10469 #endif
10470 
10471 // Win32 API IME support (for Asian languages, etc.)
10472 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10473 
10474 #include <imm.h>
10475 #ifdef _MSC_VER
10476 #pragma comment(lib, "imm32")
10477 #endif
10478 
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)10479 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10480 {
10481     // Notify OS Input Method Editor of text input position
10482     ImGuiIO& io = ImGui::GetIO();
10483     if (HWND hwnd = (HWND)io.ImeWindowHandle)
10484         if (HIMC himc = ::ImmGetContext(hwnd))
10485         {
10486             COMPOSITIONFORM cf;
10487             cf.ptCurrentPos.x = x;
10488             cf.ptCurrentPos.y = y;
10489             cf.dwStyle = CFS_FORCE_POSITION;
10490             ::ImmSetCompositionWindow(himc, &cf);
10491             ::ImmReleaseContext(hwnd, himc);
10492         }
10493 }
10494 
10495 #else
10496 
ImeSetInputScreenPosFn_DefaultImpl(int,int)10497 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10498 
10499 #endif
10500 
10501 //-----------------------------------------------------------------------------
10502 // [SECTION] METRICS/DEBUGGER WINDOW
10503 //-----------------------------------------------------------------------------
10504 // - MetricsHelpMarker() [Internal]
10505 // - ShowMetricsWindow()
10506 // - DebugNodeColumns() [Internal]
10507 // - DebugNodeDrawList() [Internal]
10508 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
10509 // - DebugNodeStorage() [Internal]
10510 // - DebugNodeTabBar() [Internal]
10511 // - DebugNodeWindow() [Internal]
10512 // - DebugNodeWindowSettings() [Internal]
10513 // - DebugNodeWindowsList() [Internal]
10514 //-----------------------------------------------------------------------------
10515 
10516 #ifndef IMGUI_DISABLE_METRICS_WINDOW
10517 
10518 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)10519 static void MetricsHelpMarker(const char* desc)
10520 {
10521     ImGui::TextDisabled("(?)");
10522     if (ImGui::IsItemHovered())
10523     {
10524         ImGui::BeginTooltip();
10525         ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
10526         ImGui::TextUnformatted(desc);
10527         ImGui::PopTextWrapPos();
10528         ImGui::EndTooltip();
10529     }
10530 }
10531 
ShowMetricsWindow(bool * p_open)10532 void ImGui::ShowMetricsWindow(bool* p_open)
10533 {
10534     if (!Begin("Dear ImGui Metrics/Debugger", p_open))
10535     {
10536         End();
10537         return;
10538     }
10539 
10540     ImGuiContext& g = *GImGui;
10541     ImGuiIO& io = g.IO;
10542     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
10543 
10544     // Basic info
10545     Text("Dear ImGui %s", ImGui::GetVersion());
10546     Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
10547     Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
10548     Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
10549     Text("%d active allocations", io.MetricsActiveAllocations);
10550     //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
10551 
10552     Separator();
10553 
10554     // Debugging enums
10555     enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
10556     const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
10557     enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
10558     const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
10559     if (cfg->ShowWindowsRectsType < 0)
10560         cfg->ShowWindowsRectsType = WRT_WorkRect;
10561     if (cfg->ShowTablesRectsType < 0)
10562         cfg->ShowTablesRectsType = TRT_WorkRect;
10563 
10564     struct Funcs
10565     {
10566         static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
10567         {
10568             if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }
10569             else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }
10570             else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }
10571             else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }
10572             else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }
10573             else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }
10574             else if (rect_type == TRT_ColumnsRect)              { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table->LastOuterHeight); }
10575             else if (rect_type == TRT_ColumnsWorkRect)          { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
10576             else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
10577             else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate
10578             else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
10579             else if (rect_type == TRT_ColumnsContentFrozen)     { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
10580             else if (rect_type == TRT_ColumnsContentUnfrozen)   { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
10581             IM_ASSERT(0);
10582             return ImRect();
10583         }
10584 
10585         static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
10586         {
10587             if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
10588             else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
10589             else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
10590             else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
10591             else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
10592             else if (rect_type == WRT_Content)       { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
10593             else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
10594             else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
10595             IM_ASSERT(0);
10596             return ImRect();
10597         }
10598     };
10599 
10600     // Tools
10601     if (TreeNode("Tools"))
10602     {
10603         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
10604         if (Button("Item Picker.."))
10605             DebugStartItemPicker();
10606         SameLine();
10607         MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
10608 
10609         Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
10610         Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
10611         SameLine();
10612         SetNextItemWidth(GetFontSize() * 12);
10613         cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
10614         if (cfg->ShowWindowsRects && g.NavWindow != NULL)
10615         {
10616             BulletText("'%s':", g.NavWindow->Name);
10617             Indent();
10618             for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
10619             {
10620                 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
10621                 Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
10622             }
10623             Unindent();
10624         }
10625         Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
10626         Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
10627 
10628         Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
10629         SameLine();
10630         SetNextItemWidth(GetFontSize() * 12);
10631         cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
10632         if (cfg->ShowTablesRects && g.NavWindow != NULL)
10633         {
10634             for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
10635             {
10636                 ImGuiTable* table = g.Tables.GetByIndex(table_n);
10637                 if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
10638                     continue;
10639 
10640                 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
10641                 if (IsItemHovered())
10642                     GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
10643                 Indent();
10644                 char buf[128];
10645                 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
10646                 {
10647                     if (rect_n >= TRT_ColumnsRect)
10648                     {
10649                         if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
10650                             continue;
10651                         for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
10652                         {
10653                             ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
10654                             ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
10655                             Selectable(buf);
10656                             if (IsItemHovered())
10657                                 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
10658                         }
10659                     }
10660                     else
10661                     {
10662                         ImRect r = Funcs::GetTableRect(table, rect_n, -1);
10663                         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
10664                         Selectable(buf);
10665                         if (IsItemHovered())
10666                             GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f);
10667                     }
10668                 }
10669                 Unindent();
10670             }
10671         }
10672 
10673         TreePop();
10674     }
10675 
10676     // Contents
10677     DebugNodeWindowsList(&g.Windows, "Windows");
10678     //DebugNodeWindowList(&g.WindowsFocusOrder, "WindowsFocusOrder");
10679     if (TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
10680     {
10681         for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
10682             DebugNodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
10683         TreePop();
10684     }
10685 
10686     // Details for Popups
10687     if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
10688     {
10689         for (int i = 0; i < g.OpenPopupStack.Size; i++)
10690         {
10691             ImGuiWindow* window = g.OpenPopupStack[i].Window;
10692             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" : "");
10693         }
10694         TreePop();
10695     }
10696 
10697     // Details for TabBars
10698     if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
10699     {
10700         for (int n = 0; n < g.TabBars.GetSize(); n++)
10701             DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar");
10702         TreePop();
10703     }
10704 
10705     // Details for Tables
10706 #ifdef IMGUI_HAS_TABLE
10707     if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
10708     {
10709         for (int n = 0; n < g.Tables.GetSize(); n++)
10710             DebugNodeTable(g.Tables.GetByIndex(n));
10711         TreePop();
10712     }
10713 #endif // #ifdef IMGUI_HAS_TABLE
10714 
10715     // Details for Docking
10716 #ifdef IMGUI_HAS_DOCK
10717     if (TreeNode("Docking"))
10718     {
10719         TreePop();
10720     }
10721 #endif // #ifdef IMGUI_HAS_DOCK
10722 
10723     // Settings
10724     if (TreeNode("Settings"))
10725     {
10726         if (SmallButton("Clear"))
10727             ClearIniSettings();
10728         SameLine();
10729         if (SmallButton("Save to memory"))
10730             SaveIniSettingsToMemory();
10731         SameLine();
10732         if (SmallButton("Save to disk"))
10733             SaveIniSettingsToDisk(g.IO.IniFilename);
10734         SameLine();
10735         if (g.IO.IniFilename)
10736             Text("\"%s\"", g.IO.IniFilename);
10737         else
10738             TextUnformatted("<NULL>");
10739         Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
10740         if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
10741         {
10742             for (int n = 0; n < g.SettingsHandlers.Size; n++)
10743                 BulletText("%s", g.SettingsHandlers[n].TypeName);
10744             TreePop();
10745         }
10746         if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
10747         {
10748             for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10749                 DebugNodeWindowSettings(settings);
10750             TreePop();
10751         }
10752 
10753 #ifdef IMGUI_HAS_TABLE
10754         if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
10755         {
10756             for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
10757                 DebugNodeTableSettings(settings);
10758             TreePop();
10759         }
10760 #endif // #ifdef IMGUI_HAS_TABLE
10761 
10762 #ifdef IMGUI_HAS_DOCK
10763 #endif // #ifdef IMGUI_HAS_DOCK
10764 
10765         if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
10766         {
10767             InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
10768             TreePop();
10769         }
10770         TreePop();
10771     }
10772 
10773     // Misc Details
10774     if (TreeNode("Internal state"))
10775     {
10776         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10777 
10778         Text("WINDOWING");
10779         Indent();
10780         Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10781         Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10782         Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
10783         Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
10784         Unindent();
10785 
10786         Text("ITEMS");
10787         Indent();
10788         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]);
10789         Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10790         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
10791         Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
10792         Unindent();
10793 
10794         Text("NAV,FOCUS");
10795         Indent();
10796         Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10797         Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
10798         Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
10799         Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
10800         Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
10801         Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
10802         Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
10803         Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
10804         Unindent();
10805 
10806         TreePop();
10807     }
10808 
10809     // Overlay: Display windows Rectangles and Begin Order
10810     if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
10811     {
10812         for (int n = 0; n < g.Windows.Size; n++)
10813         {
10814             ImGuiWindow* window = g.Windows[n];
10815             if (!window->WasActive)
10816                 continue;
10817             ImDrawList* draw_list = GetForegroundDrawList(window);
10818             if (cfg->ShowWindowsRects)
10819             {
10820                 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
10821                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10822             }
10823             if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
10824             {
10825                 char buf[32];
10826                 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
10827                 float font_size = GetFontSize();
10828                 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
10829                 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
10830             }
10831         }
10832     }
10833 
10834 #ifdef IMGUI_HAS_TABLE
10835     // Overlay: Display Tables Rectangles
10836     if (cfg->ShowTablesRects)
10837     {
10838         for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
10839         {
10840             ImGuiTable* table = g.Tables.GetByIndex(table_n);
10841             if (table->LastFrameActive < g.FrameCount - 1)
10842                 continue;
10843             ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
10844             if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
10845             {
10846                 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
10847                 {
10848                     ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
10849                     ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
10850                     float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
10851                     draw_list->AddRect(r.Min, r.Max, col, 0.0f, ~0, thickness);
10852                 }
10853             }
10854             else
10855             {
10856                 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
10857                 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10858             }
10859         }
10860     }
10861 #endif // #ifdef IMGUI_HAS_TABLE
10862 
10863 #ifdef IMGUI_HAS_DOCK
10864     // Overlay: Display Docking info
10865     if (show_docking_nodes && g.IO.KeyCtrl)
10866     {
10867     }
10868 #endif // #ifdef IMGUI_HAS_DOCK
10869 
10870     End();
10871 }
10872 
10873 // [DEBUG] Display contents of Columns
DebugNodeColumns(ImGuiOldColumns * columns)10874 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
10875 {
10876     if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
10877         return;
10878     BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
10879     for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
10880         BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
10881     TreePop();
10882 }
10883 
10884 // [DEBUG] Display contents of ImDrawList
DebugNodeDrawList(ImGuiWindow * window,const ImDrawList * draw_list,const char * label)10885 void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label)
10886 {
10887     ImGuiContext& g = *GImGui;
10888     ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
10889     int cmd_count = draw_list->CmdBuffer.Size;
10890     if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
10891         cmd_count--;
10892     bool node_open = 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, cmd_count);
10893     if (draw_list == GetWindowDrawList())
10894     {
10895         SameLine();
10896         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)
10897         if (node_open)
10898             TreePop();
10899         return;
10900     }
10901 
10902     ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
10903     if (window && IsItemHovered())
10904         fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
10905     if (!node_open)
10906         return;
10907 
10908     if (window && !window->WasActive)
10909         TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
10910 
10911     for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
10912     {
10913         if (pcmd->UserCallback)
10914         {
10915             BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
10916             continue;
10917         }
10918 
10919         char buf[300];
10920         ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
10921             pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
10922             pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
10923         bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
10924         if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
10925             DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
10926         if (!pcmd_node_open)
10927             continue;
10928 
10929         // Calculate approximate coverage area (touched pixel count)
10930         // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
10931         const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10932         const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
10933         float total_area = 0.0f;
10934         for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
10935         {
10936             ImVec2 triangle[3];
10937             for (int n = 0; n < 3; n++, idx_n++)
10938                 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
10939             total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
10940         }
10941 
10942         // Display vertex information summary. Hover to get all triangles drawn in wire-frame
10943         ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
10944         Selectable(buf);
10945         if (IsItemHovered() && fg_draw_list)
10946             DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, true, false);
10947 
10948         // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
10949         ImGuiListClipper clipper;
10950         clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
10951         while (clipper.Step())
10952             for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
10953             {
10954                 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
10955                 ImVec2 triangle[3];
10956                 for (int n = 0; n < 3; n++, idx_i++)
10957                 {
10958                     const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
10959                     triangle[n] = v.pos;
10960                     buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
10961                         (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
10962                 }
10963 
10964                 Selectable(buf, false);
10965                 if (fg_draw_list && IsItemHovered())
10966                 {
10967                     ImDrawListFlags backup_flags = fg_draw_list->Flags;
10968                     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10969                     fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
10970                     fg_draw_list->Flags = backup_flags;
10971                 }
10972             }
10973         TreePop();
10974     }
10975     TreePop();
10976 }
10977 
10978 // [DEBUG] Display mesh/aabb of a ImDrawCmd
DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow * window,const ImDrawList * draw_list,const ImDrawCmd * draw_cmd,bool show_mesh,bool show_aabb)10979 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
10980 {
10981     IM_ASSERT(show_mesh || show_aabb);
10982     ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
10983     ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10984     ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
10985 
10986     // Draw wire-frame version of all triangles
10987     ImRect clip_rect = draw_cmd->ClipRect;
10988     ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
10989     ImDrawListFlags backup_flags = fg_draw_list->Flags;
10990     fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10991     for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; )
10992     {
10993         ImVec2 triangle[3];
10994         for (int n = 0; n < 3; n++, idx_n++)
10995             vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
10996         if (show_mesh)
10997             fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
10998     }
10999     // Draw bounding boxes
11000     if (show_aabb)
11001     {
11002         fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
11003         fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
11004     }
11005     fg_draw_list->Flags = backup_flags;
11006 }
11007 
11008 // [DEBUG] Display contents of ImGuiStorage
DebugNodeStorage(ImGuiStorage * storage,const char * label)11009 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
11010 {
11011     if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
11012         return;
11013     for (int n = 0; n < storage->Data.Size; n++)
11014     {
11015         const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
11016         BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
11017     }
11018     TreePop();
11019 }
11020 
11021 // [DEBUG] Display contents of ImGuiTabBar
DebugNodeTabBar(ImGuiTabBar * tab_bar,const char * label)11022 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
11023 {
11024     // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
11025     char buf[256];
11026     char* p = buf;
11027     const char* buf_end = buf + IM_ARRAYSIZE(buf);
11028     const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
11029     p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
11030     IM_UNUSED(p);
11031     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11032     bool open = TreeNode(tab_bar, "%s", buf);
11033     if (!is_active) { PopStyleColor(); }
11034     if (is_active && IsItemHovered())
11035     {
11036         ImDrawList* draw_list = GetForegroundDrawList();
11037         draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
11038         draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11039         draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11040     }
11041     if (open)
11042     {
11043         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
11044         {
11045             const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11046             PushID(tab);
11047             if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
11048             if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
11049             Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
11050                 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth);
11051             PopID();
11052         }
11053         TreePop();
11054     }
11055 }
11056 
DebugNodeWindow(ImGuiWindow * window,const char * label)11057 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
11058 {
11059     if (window == NULL)
11060     {
11061         BulletText("%s: NULL", label);
11062         return;
11063     }
11064 
11065     ImGuiContext& g = *GImGui;
11066     const bool is_active = window->WasActive;
11067     ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
11068     if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11069     const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
11070     if (!is_active) { PopStyleColor(); }
11071     if (IsItemHovered() && is_active)
11072         GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11073     if (!open)
11074         return;
11075 
11076     if (window->MemoryCompacted)
11077         TextDisabled("Note: some memory buffers have been compacted/freed.");
11078 
11079     ImGuiWindowFlags flags = window->Flags;
11080     DebugNodeDrawList(window, window->DrawList, "DrawList");
11081     BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
11082     BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
11083         (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
11084         (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
11085         (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
11086     BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
11087     BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
11088     BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
11089     BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
11090     BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
11091     if (!window->NavRectRel[0].IsInverted())
11092         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);
11093     else
11094         BulletText("NavRectRel[0]: <None>");
11095     if (window->RootWindow != window)       { DebugNodeWindow(window->RootWindow, "RootWindow"); }
11096     if (window->ParentWindow != NULL)       { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
11097     if (window->DC.ChildWindows.Size > 0)   { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
11098     if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
11099     {
11100         for (int n = 0; n < window->ColumnsStorage.Size; n++)
11101             DebugNodeColumns(&window->ColumnsStorage[n]);
11102         TreePop();
11103     }
11104     DebugNodeStorage(&window->StateStorage, "Storage");
11105     TreePop();
11106 }
11107 
DebugNodeWindowSettings(ImGuiWindowSettings * settings)11108 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
11109 {
11110     Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
11111         settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
11112 }
11113 
11114 
DebugNodeWindowsList(ImVector<ImGuiWindow * > * windows,const char * label)11115 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
11116 {
11117     if (!TreeNode(label, "%s (%d)", label, windows->Size))
11118         return;
11119     Text("(In front-to-back order:)");
11120     for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
11121     {
11122         PushID((*windows)[i]);
11123         DebugNodeWindow((*windows)[i], "Window");
11124         PopID();
11125     }
11126     TreePop();
11127 }
11128 
11129 #else
11130 
ShowMetricsWindow(bool *)11131 void ImGui::ShowMetricsWindow(bool*) {}
DebugNodeColumns(ImGuiOldColumns *)11132 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
DebugNodeDrawList(ImGuiWindow *,const ImDrawList *,const char *)11133 void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow *,const ImDrawList *,const ImDrawCmd *,bool,bool)11134 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
DebugNodeStorage(ImGuiStorage *,const char *)11135 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
DebugNodeTabBar(ImGuiTabBar *,const char *)11136 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
DebugNodeWindow(ImGuiWindow *,const char *)11137 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
DebugNodeWindowSettings(ImGuiWindowSettings *)11138 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
DebugNodeWindowsList(ImVector<ImGuiWindow * > *,const char *)11139 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
11140 
11141 #endif
11142 
11143 //-----------------------------------------------------------------------------
11144 
11145 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
11146 // 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.
11147 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
11148 #include "imgui_user.inl"
11149 #endif
11150 
11151 //-----------------------------------------------------------------------------
11152 
11153 #endif // #ifndef IMGUI_DISABLE
11154